library(QFeatures)
library(msqrob2)
library(tibble)
library(tidyr)
library(dplyr)
library(gt)
library(plotly)
library(stageR)
library(poolr)
library(RColorBrewer)
library(seqinr)
library(stringr)
library(ExploreModelMatrix)
library(data.table)

1 Import data

df <- read.csv("evidence.txt",header=TRUE, sep = ",")
#I can ignore the sigma proteins (those were just used for QC purposes)
df <- df %>% filter(!grepl("Sigma", df$Proteins))
#filter out phospho sites with less than 75% probability
filter_rows <- sapply(df$Phospho..STY..Probabilities, function(x){
  if (!x==""){
    prob <- str_extract_all(x,  "(?<=\\().+?(?=\\))")[[1]]
    if (!any(as.double(prob) > 0.75)){return(which(df$Phospho..STY..Probabilities == x))}
  }
}, USE.NAMES = F)
filter_rows <- unique(unlist(filter_rows))
nrow(df)
## [1] 178235
df <- df[-filter_rows,]
nrow(df)
## [1] 171780
#Now get format into wide format
df_wide <- pivot_wider(df, id_cols = c("Sequence", "Modifications", "Modified.sequence", "Proteins", "Leading.proteins",
                                        "Reverse", "Potential.contaminant", "Protein.group.IDs", "Leading.razor.protein"),
                       names_from = "Raw.file", names_prefix = "Intensity_", values_from = "Intensity", values_fn = max)
ecols <- grep("Intensity",colnames(df_wide))
#order dataframe by protein, for the normalisation step
df_wide = df_wide[order(df_wide$Leading.razor.protein),]
pe <- readQFeatures(df_wide,ecol= ecols,name="peptidoformRaw")
rownames(pe[["peptidoformRaw"]]) <- rowData(pe[["peptidoformRaw"]])$Modified.sequence

1.1 Experimental Layout

metadata <- read.csv("Experimental Design.csv")
colData(pe)$file <- sapply(str_split(rownames(colData(pe)), "_"), function(x) x[[2]])
metadata$File <- sub(x=metadata$File, pattern=".raw",replacement = "")
colData(pe)$condition <- sapply(colData(pe)$file, function(x){
  metadata[metadata$File==x,]$Condition
}, USE.NAMES = F)
colData(pe)$subset <- sapply(colData(pe)$file, function(x){
  metadata[metadata$File==x,]$Subset
}, USE.NAMES = F)
colData(pe)$condition <- case_when(grepl("A", colData(pe)$condition) ~ "A",
                                        TRUE ~ "B")
colData(pe)
## DataFrame with 90 rows and 3 columns
##                            file   condition      subset
##                     <character> <character> <character>
## Intensity_QX21595GM   QX21595GM           A           y
## Intensity_QX21643GM   QX21643GM           A           y
## Intensity_QX21469GM   QX21469GM           B           x
## Intensity_QX21484GM   QX21484GM           B           x
## Intensity_QX21487GM   QX21487GM           A           x
## ...                         ...         ...         ...
## Intensity_QX21782GM   QX21782GM           A           y
## Intensity_QX21794GM   QX21794GM           A           y
## Intensity_QX21803GM   QX21803GM           B           y
## Intensity_QX21806GM   QX21806GM           A           y
## Intensity_QX21812GM   QX21812GM           A           y

1.2 Protein dataset

df_prot <- read.csv("Non-enriched/evidence.txt",header=TRUE, sep = ",")
#I can ignore the pool samples (those were just used for QC purposes)
#2 samples lack metadata info, so I will remove them for now as well
df_prot <- df_prot %>% filter(!grepl("Sigma", df_prot$Proteins),
                    #extra files not present in the ptm dataset, so not useful
                    !grepl("QE37656GM|QE37749GM|QE37769GM|QE37811GM|QE37913GM|QE37698GM|QE37781GM|QE37919GM|QE37898GM|QE37979GM|QE37713GM", df_prot$Raw.file))

#Now get format into wide format
df_prot_wide <- pivot_wider(df_prot, id_cols = c("Sequence", "Modifications", "Modified.sequence", "Proteins", "Leading.proteins",
                                        "Reverse", "Potential.contaminant", "Protein.group.IDs", "Leading.razor.protein"),
                       names_from = "Raw.file", names_prefix = "Intensity_", values_from = "Intensity", values_fn = max)
ecols <- grep("Intensity",colnames(df_prot_wide))
#order dataframe by protein, for the normalisation step
df_prot_wide = df_prot_wide[order(df_prot_wide$Leading.razor.protein),]
prot <- readQFeatures(df_prot_wide,ecol= ecols,name="proteinRaw")
rownames(prot[["proteinRaw"]]) <- rowData(prot[["proteinRaw"]])$Modified.sequence

Total protein overlap

prot_GP <- unique(df_prot_wide$Leading.razor.protein)
prot_P <- unique(df_wide$Leading.razor.protein)
#how many of the protein in enriched dataset are not in non-enriched dataset?
n_diff <- length(setdiff(prot_P, prot_GP))
n_diff/length(prot_P)
## [1] 0.2466443

25% of proteins in the enriched dataset do not find a counterpart in the non-enriched dataset

1.2.1 Experimental Layout

metadata <- read.csv("Non-enriched/metadata.csv")
colData(prot)$file <- sapply(str_split(rownames(colData(prot)), "_"), function(x) x[[2]])
metadata$File <- sapply(strsplit(metadata$File, ".", fixed = T), function(x) x[[1]])
metadata <- metadata %>% filter(!grepl("QE37656GM|QE37749GM|QE37769GM|QE37811GM|QE37913GM|QE37698GM|QE37781GM|QE37919GM|QE37898GM|QE37979GM|QE37713GM", metadata$File))
colData(prot)$condition <- sapply(colData(prot)$file, function(x){
  metadata[metadata$File==x,]$Condition
}, USE.NAMES = F)
colData(prot)$subset <- sapply(colData(prot)$file, function(x){
  metadata[metadata$File==x,]$Subset
}, USE.NAMES = F)
colData(prot)$condition <- case_when(grepl("A", colData(prot)$condition) ~ "A",
                                        TRUE ~ "B")
colData(prot)
## DataFrame with 90 rows and 3 columns
##                            file   condition      subset
##                     <character> <character> <character>
## Intensity_QE37737GM   QE37737GM           A           y
## Intensity_QE37799GM   QE37799GM           B           x
## Intensity_QE37829GM   QE37829GM           A           x
## Intensity_QE37886GM   QE37886GM           A           y
## Intensity_QE37895GM   QE37895GM           A           y
## ...                         ...         ...         ...
## Intensity_QE37988GM   QE37988GM           B           x
## Intensity_QE37892GM   QE37892GM           B           y
## Intensity_QE37907GM   QE37907GM           B           y
## Intensity_QE37991GM   QE37991GM           A           y
## Intensity_QE37949GM   QE37949GM           B           x

2 Preprocessing

2.1 PTM dataset

rowData(pe[["peptidoformRaw"]])$nNonZero <- rowSums(assay(pe[["peptidoformRaw"]]) > 0, na.rm = T)
pe <- zeroIsNA(pe, i = "peptidoformRaw")
pe <- filterFeatures(pe, ~Reverse != "+")
pe <- filterFeatures(pe, ~Potential.contaminant != "+")
pe <- logTransform(pe, base = 2, i = "peptidoformRaw", name = "peptidoformLog")
pe <- pe[rowData(pe[["peptidoformRaw"]])$nNonZero>2,,]
pe <- QFeatures::normalize(pe, method = "center.median", i = "peptidoformLog", name = "peptidoform")
colData(pe[["peptidoform"]]) <- colData(pe)

2.2 Protein dataset

rowData(prot[["proteinRaw"]])$nNonZero <- rowSums(assay(prot[["proteinRaw"]]) > 0, na.rm = T)
prot <- zeroIsNA(prot, i = "proteinRaw")
prot <- logTransform(prot, base = 2, i = "proteinRaw", name = "proteinLog")
prot <- prot[rowData(prot[["proteinRaw"]])$nNonZero>2,,]
prot <- QFeatures::normalize(prot, method = "center.median", i = "proteinLog", name = "protein")
colData(prot[["protein"]]) <- colData(prot)

2.3 Normalisation via robust summarisation

prot <- aggregateFeatures(prot,
 i = "protein",
 fcol = "Leading.razor.protein",
 na.rm = TRUE,
 name = "proteinRobust",
 fun = MsCoreUtils::robustSummary)
## Your quantitative data contain missing values. Please read the relevant
## section(s) in the aggregateFeatures manual page regarding the effects
## of missing values on data aggregation.

I have to change the colnames of the proteinRobust assay so that it can be used as a centering for the ptm dataset. I have matched the corresponding samples to each other via the metadata files (correspondance/condition column) and just changed the raw files names to the raw files names of the ptm dataset. See also rawfilenames_change.csv

rawfilenames <- read.csv("rawfilenames_change.csv")
#get current protein colnames without "Intensity_"
current_colnames <- sapply(strsplit(colnames(assay(prot[["proteinRobust"]])), "_"), function(x) x[[2]])
#replace with corresponding ptm colnames
colnames(assay(prot[["proteinRobust"]], withDimnames = F)) <- 
  sapply(current_colnames, function(x){
                  ptm_colname <- rawfilenames[rawfilenames[["protein.dataset"]]==x,]$`ptm.dataset`
                  paste0("Intensity_", ptm_colname)
                }, USE.NAMES = F)
#Only take pepforms that have a parent protein
#(when there is no global profiling dataset, this will be all peptidoforms)
pepWithProtein <- rowData(pe[["peptidoform"]])$Proteins %in% rownames(prot[["proteinRobust"]])
pePepWithProtein  <- pe[["peptidoform"]][pepWithProtein,]
pe <- addAssay(pe,pePepWithProtein,"pepformRel")
#normalisation for protein abundance step
assay(pe[["pepformRel"]]) <- assay(pe[["pepformRel"]]) - assay(prot[["proteinRobust"]], 
                                                               withDimnames = F)[rowData(pe[["pepformRel"]])$Proteins,colnames(assay(pe[["pepformRel"]]))]
boxplot(assay(pe[["pepformRel"]]))

3 Differential peptidoform usage (DPFU)

3.1 Hypothesistest for each contrast

condition A vs B

colData(pe)$condition <- relevel(as.factor(colData(pe)$condition), ref = "B")
colData(pe)$subset <- as.factor(colData(pe)$subset)
colData(pe[["pepformRel"]]) <- colData(pe)
pe <- msqrob2::msqrob(object = pe, i = "pepformRel", formula = ~condition*subset, robust=FALSE)
rowData(pe[["pepformRel"]])$msqrobModels[[2]] %>%
                  getCoef
##        (Intercept)         conditionA            subsety conditionA:subsety 
##          1.6565104         -0.5246633          0.8686482          1.2166721
fraction_of_ys <- (colData(pe) %>% as_tibble() %>% filter(subset == "y") %>% nrow()) / nrow(colData(pe))
contrasts <- c("conditionA", "conditionA + conditionA:subsety", "conditionA:subsety", "conditionA + 0.5 * conditionA:subsety", "conditionA + 0.6666667 * conditionA:subsety")
L <- makeContrast(c("conditionA = 0",
                    "conditionA + conditionA:subsety = 0",
                    "conditionA:subsety = 0",
                    "conditionA + 0.5 * conditionA:subsety = 0",
                    "conditionA + 0.6666667 * conditionA:subsety = 0"),
                  parameterNames = rowData(pe[["pepformRel"]])$msqrobModels[[2]] %>%
                  getCoef %>%
                  names)
pe <- hypothesisTest(object = pe, i = "pepformRel", contrast = L)

3.1.1 Volcanoplot

volcano <- list()
for (contrast in contrasts){
volcano[[contrast]] <- rowData(pe[["pepformRel"]])[[contrast]]%>%
            ggplot(aes(x = logFC, y = -log10(pval), 
                   color = adjPval < 0.05,
                   annotation=rowData(pe[["pepformRel"]])[,3])) +
  geom_point(cex = 2.5) +
  scale_color_manual(values = alpha(c("black", "red"), 0.5)) + 
  theme_minimal() +
  ylab("-log10(pvalue)") +
  ggtitle(contrast)
}
volcano
## $conditionA
## Warning: Removed 665 rows containing missing values (geom_point).

## 
## $`conditionA + conditionA:subsety`
## Warning: Removed 665 rows containing missing values (geom_point).

## 
## $`conditionA:subsety`
## Warning: Removed 665 rows containing missing values (geom_point).

## 
## $`conditionA + 0.5 * conditionA:subsety`
## Warning: Removed 665 rows containing missing values (geom_point).

## 
## $`conditionA + 0.6666667 * conditionA:subsety`
## Warning: Removed 665 rows containing missing values (geom_point).

3.1.2 Significant peptidoforms for each contrast

tables <- list()
for (contrast in contrasts){
sigTable <- rowData(pe[["pepformRel"]])[[contrast]]
if(nrow(sigTable <- sigTable %>%
  na.exclude %>%
  filter(adjPval<0.05))>0){
sigTable <- sigTable %>%
  na.exclude %>%
  filter(adjPval<0.05) %>%
  arrange(pval) %>%
  mutate(
    se = format(se, digits = 2), 
    df = format(df, digits =2),
    t = format(t, digits = 2),
    adjPval = format(adjPval, digits = 3),
    rank = 1:length(logFC) 
  ) 
sigTable_print <- sigTable %>% mutate(peptidoform = rownames(sigTable)) %>% gt() %>% tab_header(title = md(contrast))
tables[[contrast]] <- sigTable
#knitr::kable(sigTable, caption = contrast)
}
}
knitr::kable(tables)
logFC se df t pval adjPval rank
LVGGPM(Oxidation (M))DAS(Phospho (STY))VEEEGVRR -1.2542955 0.25 87 -5.1 0.0000023 0.00249 1
VS(Phospho (STY))REFHSHEFHSHEDM(Oxidation (M))LVVDPK -2.0161711 0.40 76 -5.0 0.0000035 0.00249 2
EFHSHEFHS(Phospho (STY))HEDM(Oxidation (M))LVVDPK -1.8682595 0.41 87 -4.5 0.0000194 0.00913 3
LPIVNFDYS(Phospho (STY))M(Oxidation (M))EEK -1.0615646 0.24 80 -4.4 0.0000393 0.01390 4
GHPQEESEESNVS(Phospho (STY))MASLGEK -1.1730632 0.27 49 -4.4 0.0000618 0.01748 5
LVGGPMDAS(Phospho (STY))VEEEGVRR -1.4414466 0.35 90 -4.1 0.0000965 0.02274 6
S(Phospho (STY))GEATDGARPQALPEPMQESK -1.3518899 0.34 85 -4.0 0.0001483 0.02798 7
EIPAWVPFDPAAQITK -2.0169090 0.45 25 -4.4 0.0001583 0.02798 8
EFHS(Phospho (STY))HEFHS(Phospho (STY))HEDM(Oxidation (M))LVVDPK -1.6948425 0.44 81 -3.9 0.0002024 0.03180 9
GILAADESTGS(Phospho (STY))IAKR -0.7129553 0.19 81 -3.8 0.0002476 0.03344 10
EFHSHEFHS(Phospho (STY))HEDMLVVDPK -2.1531285 0.56 81 -3.8 0.0002602 0.03344 11
logFC se df t pval adjPval rank
LVGGPM(Oxidation (M))DAS(Phospho (STY))VEEEGVRR -0.9202058 0.20 87 -4.6 0.0000136 0.0193 1
VS(Phospho (STY))REFHSHEFHSHEDM(Oxidation (M))LVVDPK -1.4202699 0.32 76 -4.4 0.0000358 0.0220 2
LPIVNFDYS(Phospho (STY))M(Oxidation (M))EEK -0.8670566 0.20 80 -4.3 0.0000466 0.0220 3
GHPQEESEESNVS(Phospho (STY))MASLGEK -0.9505352 0.22 49 -4.3 0.0000690 0.0244 4
GILAADESTGS(Phospho (STY))IAK -0.7224652 0.18 73 -4.0 0.0001662 0.0470 5

4 DPTM

4.1 ptm summarisatie

Summarise peptidoforms to ptm level

4.2 Determine location of the modification

fasta <- "human.fasta"
parsed_fasta <- read.fasta(file = fasta, seqtype = "AA", as.string = T)
#modified sequence column contains _ that does not matter, but hinders location determination
rowData(pe[["pepformRel"]])$modified_sequence <- gsub("_", "", rowData(pe[["pepformRel"]])$Modified.sequence)
get_ptm_location <- function(feature, data, fasta, mod_column = "Modifications", 
                             peptide_seq_column = "Sequence", mod_seq_column = "modified_sequence", 
                             protein_column = "Leading.razor.protein", split = ",", collapse = ", "){
  prot <- data[feature,][[protein_column]]
  pep_seq <- data[feature,][[peptide_seq_column]]
  mod_seq <- data[feature,][[mod_seq_column]]
  prot_seq <- fasta[[prot]][1]

  #find location of peptide in protein
  #add -1 here, so that the addition of the location later on is correct
  pep_location <- unlist(lapply(gregexpr(pattern = pep_seq, prot_seq), min)) - 1
  final_mod <- c()
  j <- mod_seq
  #go over the modifications inside the modified sequence
  for(mod in regmatches(mod_seq, gregexpr("\\(.*?\\)\\)", mod_seq, perl=T))[[1]]){
    #find location of modification in peptide
    mod_location <- unlist(lapply(gregexpr(pattern = mod, j, fixed = T), min))
    #get location in protein (-1 because else it gives you the location after because of the presence of the modification in the string)
    location <- mod_location + pep_location -1
    #add location to modification
    mod_ <- paste(mod, location)
    #save modification
    final_mod <- c(final_mod, mod_)
    #now remove current modification from the sequence, so that we can continue to the next mod
    str_sub(j, mod_location, nchar(mod)+mod_location-1) <- ""
  }
  return(paste(final_mod, collapse = collapse))
}
rowData(pe[["pepformRel"]])$mod <- sapply(rownames(rowData(pe[["pepformRel"]])), function(x){
    get_ptm_location(x, rowData(pe[["pepformRel"]]), parsed_fasta)
}, USE.NAMES = F)
#Add ptm variable = protein + modification
rowData(pe[["pepformRel"]])$ptm <- ifelse(rowData(pe[["pepformRel"]])$mod != "",
                                               paste(rowData(pe[["pepformRel"]])$Leading.razor.protein, rowData(pe[["pepformRel"]])$mod, sep="_"), 
                                               "")

4.2.1 Get ptm level intensity matrix

Get matrix for DPTMx -> get summarised intensity values

prots <- unique(rowData(pe[["pepformRel"]])$Leading.razor.protein)
#Do for each protein
ptms <- sapply(prots, function(i) {
  pe_sub <- pe[["pepformRel"]][grepl(i, rowData(pe[["pepformRel"]])$Leading.razor.protein, fixed = T),]
  #pe_sub <- filterFeatures(pe,~grepl(Leading.razor.protein,pattern=i,fixed = T))
  #Get all unique modification present on that protein
  mods <- unique(unlist(strsplit(rowData(pe_sub)$mod, split = ", ", fixed = TRUE)))
  #Add protein info to mods
  ptm <- paste(rep(i, length(mods)), mods)
  #return all the protein-mods combinations
  ptm
})
ptms <- as.vector(unlist(ptms))
#For each ptm do
ptm_x_assay <- sapply(seq(1:length(ptms)), function(i){ 
  x <- ptms[i]
  #Get current protein and mod from ptm
  prot <- str_split(x, " ", 2)[[1]][1]
  current_ptm <- str_split(x, " ", 2)[[1]][2]
  #filter on that protein and on that mod to obtain all peptidoforms that correspond to the ptm
  pe_sub <- pe[["pepformRel"]][grepl(prot, rowData(pe[["pepformRel"]])$Leading.razor.protein, fixed = T),]
  #pe_sub <- filterFeatures(pe,~grepl(Leading.razor.protein,pattern=prot, fixed = T))
  ptm_sub <- pe_sub[grepl(current_ptm, rowData(pe_sub)$mod, fixed = T),]
  #ptm_sub <- filterFeatures(pe_sub,~grepl(mod,pattern=current_ptm, fixed=T))[["peptidoformNorm"]]
  #Get intensity values of those peptidoforms
  y <- assay(ptm_sub)
  #And summarise them to 1 row of intensity values: 1 value per sample for that ptm
  ptm_y <- try(MsCoreUtils::robustSummary(y), silent = T)
  if (is(ptm_y, "try-error")){
    ptm_y <- rep(NA, ncol(y))}
  ptm_y
})
## Warning in rlm.default(X, expression, ...): 'rlm' failed to converge in 20 steps

## Warning in rlm.default(X, expression, ...): 'rlm' failed to converge in 20 steps

## Warning in rlm.default(X, expression, ...): 'rlm' failed to converge in 20 steps

## Warning in rlm.default(X, expression, ...): 'rlm' failed to converge in 20 steps
#Then we get the intensity assay on ptm level
ptm_x_assay <- t(ptm_x_assay)
rownames(ptm_x_assay) <- ptms

4.2.2 Add to QFeatures object

Filter out ptms with all zero intensities

print(paste(nrow(ptm_x_assay), "ptms before filtering"))
## [1] "430 ptms before filtering"
filtering <- rowSums(ptm_x_assay != 0, na.rm=TRUE) > 0 
ptm_x_assay <- ptm_x_assay[filtering,]
print(paste(nrow(ptm_x_assay), "ptms after filtering"))
## [1] "427 ptms after filtering"
all(rownames(colData(pe)) == colnames(ptm_x_assay))
## [1] TRUE
rowdata <- data.frame(ptm = rownames(ptm_x_assay))
rowdata$protein <- sapply(str_split(rowdata$ptm, pattern=" "),function(x) x[1])
ptm_y_assay <- SummarizedExperiment(assays=as.matrix(ptm_x_assay), rowData=rowdata, colData=colData(pe))
pe <- addAssay(pe, ptm_y_assay, "ptmRel")

4.3 Hypothesistest for each contrast

colData(pe[["ptmRel"]])
## DataFrame with 90 rows and 3 columns
##                            file condition   subset
##                     <character>  <factor> <factor>
## Intensity_QX21595GM   QX21595GM         A        y
## Intensity_QX21643GM   QX21643GM         A        y
## Intensity_QX21469GM   QX21469GM         B        x
## Intensity_QX21484GM   QX21484GM         B        x
## Intensity_QX21487GM   QX21487GM         A        x
## ...                         ...       ...      ...
## Intensity_QX21782GM   QX21782GM         A        y
## Intensity_QX21794GM   QX21794GM         A        y
## Intensity_QX21803GM   QX21803GM         B        y
## Intensity_QX21806GM   QX21806GM         A        y
## Intensity_QX21812GM   QX21812GM         A        y
pe <- msqrob2::msqrob(object = pe, i = "ptmRel", formula = ~condition*subset, robust=FALSE, overwrite = T)
rowData(pe[["ptmRel"]])$msqrobModels[[2]] %>%
                  getCoef
##        (Intercept)         conditionA            subsety conditionA:subsety 
##         -0.5032653         -0.1630285          1.2483607         -0.2654357
contrasts <- c("conditionA", "conditionA + conditionA:subsety", "conditionA:subsety", "conditionA + 0.5 * conditionA:subsety", "conditionA + 0.6666667 * conditionA:subsety")
L <- makeContrast(c("conditionA = 0",
                    "conditionA + conditionA:subsety = 0",
                    "conditionA:subsety = 0",
                    "conditionA + 0.5 * conditionA:subsety = 0",
                    "conditionA + 0.6666667 * conditionA:subsety = 0"),   
                  parameterNames = rowData(pe[["ptmRel"]])$msqrobModels[[3]] %>%
                  getCoef %>%
                  names)
pe <- hypothesisTest(object = pe, i = "ptmRel", contrast = L, overwrite = T)

4.3.1 Volcanoplot

volcanos <- list()
for (contrast in contrasts){
library(plotly)
volcanos[[contrast]] <- 
  rowData(pe[["ptmRel"]])[[contrast]]%>%
  ggplot(aes(x = logFC, y = -log10(pval), 
             color = adjPval < 0.05,
             annotation=rowData(pe[["ptmRel"]])[,3])) +
  geom_point(cex = 2.5) +
  scale_color_manual(values = alpha(c("black", "red"), 0.5)) + theme_minimal() +
  ylab("-log10(pvalue)") +
  ggtitle(contrast)
}
volcanos
## $conditionA
## Warning: Removed 57 rows containing missing values (geom_point).

## 
## $`conditionA + conditionA:subsety`
## Warning: Removed 57 rows containing missing values (geom_point).

## 
## $`conditionA:subsety`
## Warning: Removed 57 rows containing missing values (geom_point).

## 
## $`conditionA + 0.5 * conditionA:subsety`
## Warning: Removed 57 rows containing missing values (geom_point).

## 
## $`conditionA + 0.6666667 * conditionA:subsety`
## Warning: Removed 57 rows containing missing values (geom_point).

4.3.2 Significant ptms for each contrast

tables <- list()
for (contrast in contrasts){
sigTable <- rowData(pe[["ptmRel"]])[[contrast]]
if(nrow(sigTable %>%
  na.exclude %>%
  filter(adjPval<0.05)) > 0){
sigTable <- sigTable %>%
  na.exclude %>%
  filter(adjPval<0.05) %>%
  arrange(pval) %>%
  mutate(
    se = format(se, digits = 2), 
    df = format(df, digits =2),
    t = format(t, digits = 2),
    adjPval = format(adjPval, digits = 3),
    rank = 1:length(logFC) 
  ) 
sigTable_print <- sigTable %>% mutate(peptidoform = rownames(sigTable)) %>% gt() %>% tab_header(title = md(contrast))
tables[[contrast]] <- sigTable
}
}
knitr::kable(tables)
logFC se df t pval adjPval rank
sp|P10451|OSTP_HUMAN (Oxidation (M)) 284 -1.4659656 0.31 89 -4.8 0.0000075 0.00279 1
sp|P01034|CYTC_HUMAN (Oxidation (M)) 40 -0.9714811 0.22 88 -4.3 0.0000393 0.00509 2
sp|O94769|ECM2_HUMAN (Oxidation (M)) 76 -1.0615646 0.24 79 -4.3 0.0000413 0.00509 3
sp|P10645|CMGA_HUMAN (Phospho (STY)) 161 -1.2783351 0.30 61 -4.2 0.0000795 0.00735 4
sp|P10645|CMGA_HUMAN (Phospho (STY)) 142 -1.2574547 0.31 84 -4.1 0.0001125 0.00753 5
sp|P04075|ALDOA_HUMAN (Phospho (STY)) 39 -0.7100813 0.18 88 -4.0 0.0001222 0.00753 6
sp|P05060|SCG1_HUMAN (Phospho (STY)) 317 -1.1385927 0.30 70 -3.8 0.0002791 0.01475 7
sp|P10451|OSTP_HUMAN (Phospho (STY)) 280 -1.7898662 0.50 89 -3.6 0.0005233 0.02275 8
sp|O94769|ECM2_HUMAN (Phospho (STY)) 75 -1.1008025 0.31 89 -3.6 0.0005534 0.02275 9
sp|P02774|VTDB_HUMAN (Phospho (STY)) 95 -0.7719926 0.22 63 -3.6 0.0006759 0.02501 10
sp|P01042|KNG1_HUMAN (Phospho (STY)) 275 -1.3530778 0.38 39 -3.6 0.0009266 0.02893 11
sp|P51693|APLP1_HUMAN (Phospho (STY)) 515 -1.2953037 0.38 83 -3.4 0.0009384 0.02893 12
sp|P10451|OSTP_HUMAN (Phospho (STY)) 27 -1.2549447 0.37 89 -3.4 0.0010416 0.02964 13
sp|P10645|CMGA_HUMAN (Phospho (STY)) 218 -0.9143803 0.27 89 -3.4 0.0011272 0.02978 14
sp|P13521|SCG2_HUMAN (Phospho (STY)) 106 -1.2377539 0.36 48 -3.4 0.0012072 0.02978 15
sp|P02765|FETUA_HUMAN (Oxidation (M)) 321 -1.4433264 0.45 79 -3.2 0.0017541 0.03823 16
sp|P09972|ALDOC_HUMAN (Phospho (STY)) 39 -1.1682749 0.36 73 -3.2 0.0019930 0.03823 17
sp|P19823|ITIH2_HUMAN (Oxidation (M)) 64 -2.4624647 0.69 19 -3.6 0.0020215 0.03823 18
sp|P10451|OSTP_HUMAN (Phospho (STY)) 24 -0.9663830 0.30 62 -3.2 0.0021278 0.03823 19
sp|P10909|CLUS_HUMAN (Phospho (STY)) 210 -1.3693705 0.42 38 -3.3 0.0021492 0.03823 20
sp|P00747|PLMN_HUMAN (Phospho (STY)) 358 -0.9781689 0.30 58 -3.2 0.0021699 0.03823 21
sp|P24592|IBP6_HUMAN (Phospho (STY)) 152 -0.8601732 0.28 86 -3.1 0.0025077 0.04098 22
sp|P10451|OSTP_HUMAN (Phospho (STY)) 195 -1.2466403 0.40 76 -3.1 0.0025475 0.04098 23
sp|P01034|CYTC_HUMAN (Phospho (STY)) 43 -0.9600921 0.31 90 -3.1 0.0027547 0.04247 24
sp|P24593|IBP5_HUMAN (Phospho (STY)) 124 -0.8831684 0.29 66 -3.1 0.0029382 0.04348 25
sp|P10645|CMGA_HUMAN (Phospho (STY)) 370 -0.9174091 0.29 35 -3.2 0.0032049 0.04561 26
sp|P61769|B2MG_HUMAN (Phospho (STY)) 108 -0.6020597 0.20 87 -3.0 0.0034231 0.04592 27
sp|P30086|PEBP1_HUMAN (Phospho (STY)) 52 -0.9909364 0.33 80 -3.0 0.0034749 0.04592 28
sp|P04075|ALDOA_HUMAN (Phospho (STY)) 36 -1.2092350 0.40 79 -3.0 0.0037198 0.04746 29
sp|P05060|SCG1_HUMAN (Phospho (STY)) 311 -0.8607261 0.29 90 -3.0 0.0039602 0.04884 30
logFC se df t pval adjPval rank
sp|P04075|ALDOA_HUMAN (Phospho (STY)) 39 -0.6427094 0.14 88 -4.5 0.0000246 0.00905 1
sp|O94769|ECM2_HUMAN (Oxidation (M)) 76 -0.8670566 0.20 79 -4.3 0.0000489 0.00905 2
sp|P01034|CYTC_HUMAN (Oxidation (M)) 40 -0.7164067 0.18 88 -3.9 0.0001623 0.02002 3
sp|P10451|OSTP_HUMAN (Oxidation (M)) 284 -0.9637083 0.25 89 -3.8 0.0002268 0.02098 4
sp|P10645|CMGA_HUMAN (Phospho (STY)) 142 -0.9261445 0.25 84 -3.7 0.0003919 0.02900 5
sp|P02774|VTDB_HUMAN (Phospho (STY)) 95 -0.6487798 0.18 63 -3.7 0.0005014 0.03092 6
sp|P30086|PEBP1_HUMAN (Phospho (STY)) 52 -0.9408715 0.26 80 -3.6 0.0006198 0.03276 7
sp|P09972|ALDOC_HUMAN (Phospho (STY)) 39 -1.0004081 0.29 73 -3.5 0.0008802 0.04071 8
sp|Q14515|SPRL1_HUMAN (Oxidation (M)) 276 -1.5307028 0.43 35 -3.5 0.0012155 0.04822 9
sp|P10645|CMGA_HUMAN (Phospho (STY)) 218 -0.7337973 0.22 89 -3.3 0.0013032 0.04822 10
sp|P10645|CMGA_HUMAN (Phospho (STY)) 161 -0.8365480 0.25 61 -3.3 0.0014779 0.04971 11
sp|P04075|ALDOA_HUMAN (Phospho (STY)) 36 -1.0796746 0.33 79 -3.3 0.0016171 0.04986 12

5 PLOTS

5.0.1 Lineplots

5.0.1.1 PTM - level

ptm_list1 <- c()
for (contrast in contrasts){
sigTable <- rowData(pe[["ptmRel"]])[[contrast]] %>% filter(adjPval<0.05)
ptm_list1 <- c(ptm_list1, rownames(sigTable))
}
ptm_list1 <- unique(ptm_list1)

plots <- list()
for (i in ptm_list1){
prot_ = str_split(i, " ")[[1]][1]
site_ = str_split_fixed(i, " ", 2)[,2]

pepform_df <- longFormat(pe[,,"pepformRel"], rowvars = c("Leading.razor.protein", "ptm"), colvars = c("condition", "subset")) %>% as.data.frame()
pepform_df <- pepform_df %>% filter(Leading.razor.protein == prot_)
pepform_df <- pepform_df %>% filter(grepl(site_, ptm, fixed = T))
pepform_df$FeatureType <- "Peptide"
pepform_df <- pepform_df %>% arrange(condition, subset, rowname)

ptm_df <- longFormat(pe[,,"ptmRel"], rowvars = c("protein", "ptm"), colvars = c("condition", "subset")) %>% as.data.frame()
ptm_df <- ptm_df %>% filter(ptm == i)
ptm_df$FeatureType <- "PTM"
ptm_df <- ptm_df %>% arrange(condition, subset, rowname)

ptm_estimate <- ptm_df %>% select(c("primary", "colname", "condition", "subset")) %>%
                           mutate(rowname = paste(ptm_df$rowname, "estimate"),
                                  ptm = paste(ptm_df$ptm, "estimate"),
                                  Protein = paste(ptm_df$Protein, "estimate"),
                                  FeatureType = "PTM_estimated",
                                  value = NA,
                                  assay = "model")
fixeffects <- rowData(pe[["ptmRel"]])$msqrobModels[[unique(ptm_df$ptm)]] %>% getCoef
ptm_estimate[ptm_estimate$condition=="B"&(ptm_estimate$subset=="x"),]$value <- 
                                                    fixeffects[["(Intercept)"]]
ptm_estimate[ptm_estimate$condition=="B"&(ptm_estimate$subset=="y"),]$value <- 
   fixeffects[["(Intercept)"]] + fixeffects[["subsety"]]
ptm_estimate[ptm_estimate$condition=="A"&(ptm_estimate$subset=="x"),]$value <- 
   fixeffects[["(Intercept)"]] + fixeffects[["conditionA"]]
ptm_estimate[ptm_estimate$condition=="A"&(ptm_estimate$subset=="y"),]$value <- 
   fixeffects[["(Intercept)"]] + fixeffects[["conditionA"]] + fixeffects[["conditionA:subsety"]] + fixeffects[["subsety"]]

plot_df <- rbindlist(list(pepform_df, ptm_df, ptm_estimate), fill = TRUE)
plot_points <- rbindlist(list(pepform_df, ptm_df), fill = TRUE)

plot_df$primary <- forcats::fct_inorder(plot_df$primary)

plots[[i]] <- plot_df %>% ggplot() +
  geom_line(aes(x = primary, y = value , group = rowname, color = FeatureType), size = 2) +
  geom_point(data = plot_points, aes(x = primary, y = value , group = rowname, 
                                     color = FeatureType), size = 5) +
  geom_vline(data=data.frame(x = c(43.5, 17.5, 56.5)),
             aes(xintercept=as.numeric(x)), linetype = "dashed") +
  scale_colour_manual(values = c("Peptide" = "#C3C3C3", "PTM" = "palegreen3",
                                 "PTM_estimated" = "slateblue3")) +
  labs(title = i, x = "Sample", y = "Intensity") +
  theme_light() +
  theme(axis.text.x = element_text(angle = 60, hjust=1, size = 10),
        axis.text.y = element_text(size = 16),
        legend.text=element_text(size=19),
        axis.title.y = element_text(size = 16),
        axis.title.x = element_text(size = 16),
        title = element_text(size = 19),
        strip.text = element_text(size = 16),
        legend.title =  element_blank(),
        legend.direction = "horizontal",
        legend.position = c(0.5, 0.1)) +
  annotate("text", x = 24, y = 6.5, label = "B", size = 8) +
  annotate("text", x = 70, y = 6.5, label = "A", size = 8) +
  annotate("text", x = 9, y = 6, label = "x", size = 7) +
  annotate("text", x = 30, y = 6, label = "y", size = 7) +
  annotate("text", x = 49.5, y = 6, label = "x", size = 7) +
  annotate("text", x = 73, y = 6, label = "y", size = 7) +
  ylim(-6, 6.5)

}
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
plots
## $`sp|O94769|ECM2_HUMAN (Phospho (STY)) 75`
## Warning: Removed 13 rows containing missing values (geom_point).

## 
## $`sp|O94769|ECM2_HUMAN (Oxidation (M)) 76`
## Warning: Removed 22 rows containing missing values (geom_point).

## 
## $`sp|P00747|PLMN_HUMAN (Phospho (STY)) 358`
## Warning: Removed 4 row(s) containing missing values (geom_path).
## Warning: Removed 64 rows containing missing values (geom_point).

## 
## $`sp|P01034|CYTC_HUMAN (Oxidation (M)) 40`
## Warning: Removed 29 row(s) containing missing values (geom_path).
## Warning: Removed 159 rows containing missing values (geom_point).

## 
## $`sp|P01034|CYTC_HUMAN (Phospho (STY)) 43`
## Warning: Removed 5 row(s) containing missing values (geom_path).
## Warning: Removed 40 rows containing missing values (geom_point).

## 
## $`sp|P01042|KNG1_HUMAN (Phospho (STY)) 275`
## Warning: Removed 6 row(s) containing missing values (geom_path).
## Warning: Removed 102 rows containing missing values (geom_point).

## 
## $`sp|P02765|FETUA_HUMAN (Oxidation (M)) 321`
## Warning: Removed 22 rows containing missing values (geom_point).

## 
## $`sp|P02774|VTDB_HUMAN (Phospho (STY)) 95`
## Warning: Removed 54 rows containing missing values (geom_point).

## 
## $`sp|P04075|ALDOA_HUMAN (Phospho (STY)) 39`
## Warning: Removed 1 row(s) containing missing values (geom_path).
## Warning: Removed 30 rows containing missing values (geom_point).

## 
## $`sp|P04075|ALDOA_HUMAN (Phospho (STY)) 36`
## Warning: Removed 22 rows containing missing values (geom_point).

## 
## $`sp|P05060|SCG1_HUMAN (Phospho (STY)) 311`
## Warning: Removed 1 row(s) containing missing values (geom_path).
## Warning: Removed 61 rows containing missing values (geom_point).

## 
## $`sp|P05060|SCG1_HUMAN (Phospho (STY)) 317`
## Warning: Removed 7 row(s) containing missing values (geom_path).
## Warning: Removed 97 rows containing missing values (geom_point).

## 
## $`sp|P09972|ALDOC_HUMAN (Phospho (STY)) 39`
## Warning: Removed 2 row(s) containing missing values (geom_path).
## Warning: Removed 34 rows containing missing values (geom_point).

## 
## $`sp|P10451|OSTP_HUMAN (Oxidation (M)) 284`
## Warning: Removed 37 row(s) containing missing values (geom_path).
## Warning: Removed 292 rows containing missing values (geom_point).

## 
## $`sp|P10451|OSTP_HUMAN (Phospho (STY)) 280`
## Warning: Removed 28 row(s) containing missing values (geom_path).
## Warning: Removed 269 rows containing missing values (geom_point).

## 
## $`sp|P10451|OSTP_HUMAN (Phospho (STY)) 24`
## Warning: Removed 52 row(s) containing missing values (geom_path).
## Warning: Removed 206 rows containing missing values (geom_point).

## 
## $`sp|P10451|OSTP_HUMAN (Phospho (STY)) 27`
## Warning: Removed 54 row(s) containing missing values (geom_path).
## Warning: Removed 531 rows containing missing values (geom_point).

## 
## $`sp|P10451|OSTP_HUMAN (Phospho (STY)) 195`
## Warning: Removed 102 row(s) containing missing values (geom_path).
## Warning: Removed 485 rows containing missing values (geom_point).

## 
## $`sp|P10645|CMGA_HUMAN (Phospho (STY)) 218`
## Warning: Removed 2 rows containing missing values (geom_point).

## 
## $`sp|P10645|CMGA_HUMAN (Phospho (STY)) 370`
## Warning: Removed 12 row(s) containing missing values (geom_path).
## Warning: Removed 110 rows containing missing values (geom_point).

## 
## $`sp|P10645|CMGA_HUMAN (Phospho (STY)) 161`
## Warning: Removed 17 row(s) containing missing values (geom_path).
## Warning: Removed 134 rows containing missing values (geom_point).

## 
## $`sp|P10645|CMGA_HUMAN (Phospho (STY)) 142`
## Warning: Removed 12 row(s) containing missing values (geom_path).
## Warning: Removed 72 rows containing missing values (geom_point).

## 
## $`sp|P10909|CLUS_HUMAN (Phospho (STY)) 210`
## Warning: Removed 106 rows containing missing values (geom_point).

## 
## $`sp|P13521|SCG2_HUMAN (Phospho (STY)) 106`
## Warning: Removed 23 row(s) containing missing values (geom_path).
## Warning: Removed 168 rows containing missing values (geom_point).

## 
## $`sp|P19823|ITIH2_HUMAN (Oxidation (M)) 64`
## Warning: Removed 141 row(s) containing missing values (geom_path).
## Warning: Removed 317 rows containing missing values (geom_point).

## 
## $`sp|P24592|IBP6_HUMAN (Phospho (STY)) 152`
## Warning: Removed 13 row(s) containing missing values (geom_path).
## Warning: Removed 112 rows containing missing values (geom_point).

## 
## $`sp|P24593|IBP5_HUMAN (Phospho (STY)) 124`
## Warning: Removed 30 row(s) containing missing values (geom_path).
## Warning: Removed 197 rows containing missing values (geom_point).

## 
## $`sp|P30086|PEBP1_HUMAN (Phospho (STY)) 52`
## Warning: Removed 20 rows containing missing values (geom_point).

## 
## $`sp|P51693|APLP1_HUMAN (Phospho (STY)) 515`
## Warning: Removed 14 rows containing missing values (geom_point).

## 
## $`sp|P61769|B2MG_HUMAN (Phospho (STY)) 108`
## Warning: Removed 6 rows containing missing values (geom_point).

## 
## $`sp|Q14515|SPRL1_HUMAN (Oxidation (M)) 276`
## Warning: Removed 26 row(s) containing missing values (geom_path).
## Warning: Removed 194 rows containing missing values (geom_point).

5.0.1.2 Peptidoform level

pepf_list1 <- c()
for (contrast in contrasts){
sigTable <- rowData(pe[["pepformRel"]])[[contrast]] %>% filter(adjPval<0.05)
pepf_list1 <- c(pepf_list1, rownames(sigTable))
}
pepf_list1 <- unique(pepf_list1)

plots <- list()
for (i in pepf_list1){
prot_ = rowData(pe[["pepformRel"]])[i,][["Leading.razor.protein"]]
site_ = rowData(pe[["pepformRel"]])[i,][["mod"]]
sites_ = strsplit(site_, ", ")[[1]]
for (site_ in sites_){
ptm_ = paste(prot_, site_, collapse = " ")
print(ptm_)

pepform_df <- longFormat(pe[,,"pepformRel"], rowvars = c("Leading.razor.protein", "ptm"), colvars = c("condition", "subset")) %>% as.data.frame()
pepform_df <- pepform_df %>% filter(Leading.razor.protein == prot_)
pepform_df <- pepform_df %>% filter(grepl(site_, ptm, fixed = T))
pepform_df$FeatureType <- "Peptide"
pepform_df[pepform_df$rowname==i,]$FeatureType <- "SignificantPeptide"
pepform_df <- pepform_df %>% arrange(condition, subset, rowname)

ptm_df <- longFormat(pe[,,"ptmRel"], rowvars = c("protein", "ptm"), colvars = c("condition", "subset")) %>% as.data.frame()
ptm_df <- ptm_df %>% filter(ptm == ptm_)
ptm_df$FeatureType <- "PTM"
ptm_df <- ptm_df %>% arrange(condition, subset, rowname)

ptm_estimate <- ptm_df %>% select(c("primary", "colname", "condition", "subset")) %>%
                           mutate(rowname = paste(ptm_df$rowname, "estimate"),
                                  ptm = paste(ptm_df$ptm, "estimate"),
                                  Protein = paste(ptm_df$Protein, "estimate"),
                                  FeatureType = "PTM_estimated",
                                  value = NA,
                                  assay = "model")
fixeffects <- rowData(pe[["ptmRel"]])$msqrobModels[[unique(ptm_df$ptm)]] %>% getCoef
ptm_estimate[ptm_estimate$condition=="B"&(ptm_estimate$subset=="x"),]$value <- 
                                                    fixeffects[["(Intercept)"]]
ptm_estimate[ptm_estimate$condition=="B"&(ptm_estimate$subset=="y"),]$value <- 
   fixeffects[["(Intercept)"]] + fixeffects[["subsety"]]
ptm_estimate[ptm_estimate$condition=="A"&(ptm_estimate$subset=="x"),]$value <- 
   fixeffects[["(Intercept)"]] + fixeffects[["conditionA"]]
ptm_estimate[ptm_estimate$condition=="A"&(ptm_estimate$subset=="y"),]$value <- 
   fixeffects[["(Intercept)"]] + fixeffects[["conditionA"]] + fixeffects[["conditionA:subsety"]] + fixeffects[["subsety"]]


plot_df <- rbindlist(list(pepform_df, ptm_df, ptm_estimate), fill = TRUE)
plot_points <- rbindlist(list(pepform_df, ptm_df), fill = TRUE)


plot_df$primary <- forcats::fct_inorder(plot_df$primary)

plots[[paste(i, site_)]] <- plot_df %>% ggplot() +
  geom_line(aes(x = primary, y = value , group = rowname, color = FeatureType), size =2) +
  geom_point(data = plot_points, aes(x = primary, y = value , group = rowname, color = FeatureType), size = 5) +
  geom_vline(data=data.frame(x = c(43.5, 17.5, 56.5)),
             aes(xintercept=as.numeric(x)), linetype = "dashed") +
  scale_colour_manual(values = c("Peptide" = "#C3C3C3", "PTM" = "palegreen3", 
                                 "SignificantPeptide" = "violetred1",
                                 "PTM_estimated" = "lightgoldenrod")) +
  labs(title = paste(i, ptm_), x = "Sample", y = "Intensity") +
  theme_light() +
  theme(axis.text.x = element_text(angle = 60, hjust=1, size = 10),
        axis.text.y = element_text(size = 16),
        legend.text=element_text(size=19),
        axis.title.y = element_text(size = 16),
        axis.title.x = element_text(size = 16),
        title = element_text(size = 19),
        strip.text = element_text(size = 16),
        legend.title =  element_blank(),
        legend.direction = "horizontal",
        legend.position = c(0.5, 0.1)) +
  annotate("text", x = 24, y = 6.5, label = "B", size = 8) +
  annotate("text", x = 70, y = 6.5, label = "A", size = 8) +
  annotate("text", x = 9, y = 6, label = "x", size = 7) +
  annotate("text", x = 30, y = 6, label = "y", size = 7) +
  annotate("text", x = 49.5, y = 6, label = "x", size = 7) +
  annotate("text", x = 73, y = 6, label = "y", size = 7) +
  ylim(-6, 6.5)
}
}
## [1] "sp|O94769|ECM2_HUMAN (Phospho (STY)) 75"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|O94769|ECM2_HUMAN (Oxidation (M)) 76"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P01034|CYTC_HUMAN (Phospho (STY)) 43"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P01034|CYTC_HUMAN (Oxidation (M)) 40"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P01034|CYTC_HUMAN (Phospho (STY)) 43"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P04075|ALDOA_HUMAN (Phospho (STY)) 39"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P05060|SCG1_HUMAN (Phospho (STY)) 317"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Phospho (STY)) 280"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Phospho (STY)) 275"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Phospho (STY)) 280"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Oxidation (M)) 284"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Phospho (STY)) 280"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Oxidation (M)) 284"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Phospho (STY)) 270"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10451|OSTP_HUMAN (Oxidation (M)) 284"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P10645|CMGA_HUMAN (Phospho (STY)) 142"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## [1] "sp|P04075|ALDOA_HUMAN (Phospho (STY)) 39"
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
## Warning: 'experiments' dropped; see 'metadata'
## harmonizing input:
##   removing 360 sampleMap rows not in names(experiments)
plots
## $`_LPIVNFDYS(Phospho (STY))M(Oxidation (M))EEK_ (Phospho (STY)) 75`
## Warning: Removed 13 rows containing missing values (geom_point).

## 
## $`_LPIVNFDYS(Phospho (STY))M(Oxidation (M))EEK_ (Oxidation (M)) 76`
## Warning: Removed 22 rows containing missing values (geom_point).

## 
## $`_LVGGPMDAS(Phospho (STY))VEEEGVRR_ (Phospho (STY)) 43`
## Warning: Removed 5 row(s) containing missing values (geom_path).
## Warning: Removed 40 rows containing missing values (geom_point).

## 
## $`_LVGGPM(Oxidation (M))DAS(Phospho (STY))VEEEGVRR_ (Oxidation (M)) 40`
## Warning: Removed 29 row(s) containing missing values (geom_path).
## Warning: Removed 159 rows containing missing values (geom_point).

## 
## $`_LVGGPM(Oxidation (M))DAS(Phospho (STY))VEEEGVRR_ (Phospho (STY)) 43`
## Warning: Removed 5 row(s) containing missing values (geom_path).
## Warning: Removed 40 rows containing missing values (geom_point).

## 
## $`_GILAADESTGS(Phospho (STY))IAKR_ (Phospho (STY)) 39`
## Warning: Removed 1 row(s) containing missing values (geom_path).
## Warning: Removed 30 rows containing missing values (geom_point).

## 
## $`_GHPQEESEESNVS(Phospho (STY))MASLGEK_ (Phospho (STY)) 317`
## Warning: Removed 7 row(s) containing missing values (geom_path).
## Warning: Removed 97 rows containing missing values (geom_point).

## 
## $`_EFHSHEFHS(Phospho (STY))HEDMLVVDPK_ (Phospho (STY)) 280`
## Warning: Removed 28 row(s) containing missing values (geom_path).
## Warning: Removed 269 rows containing missing values (geom_point).

## 
## $`_EFHS(Phospho (STY))HEFHS(Phospho (STY))HEDM(Oxidation (M))LVVDPK_ (Phospho (STY)) 275`
## Warning: Removed 15 row(s) containing missing values (geom_path).
## Warning: Removed 238 rows containing missing values (geom_point).

## 
## $`_EFHS(Phospho (STY))HEFHS(Phospho (STY))HEDM(Oxidation (M))LVVDPK_ (Phospho (STY)) 280`
## Warning: Removed 28 row(s) containing missing values (geom_path).
## Warning: Removed 269 rows containing missing values (geom_point).

## 
## $`_EFHS(Phospho (STY))HEFHS(Phospho (STY))HEDM(Oxidation (M))LVVDPK_ (Oxidation (M)) 284`
## Warning: Removed 37 row(s) containing missing values (geom_path).
## Warning: Removed 292 rows containing missing values (geom_point).

## 
## $`_EFHSHEFHS(Phospho (STY))HEDM(Oxidation (M))LVVDPK_ (Phospho (STY)) 280`
## Warning: Removed 28 row(s) containing missing values (geom_path).
## Warning: Removed 269 rows containing missing values (geom_point).

## 
## $`_EFHSHEFHS(Phospho (STY))HEDM(Oxidation (M))LVVDPK_ (Oxidation (M)) 284`
## Warning: Removed 37 row(s) containing missing values (geom_path).
## Warning: Removed 292 rows containing missing values (geom_point).

## 
## $`_VS(Phospho (STY))REFHSHEFHSHEDM(Oxidation (M))LVVDPK_ (Phospho (STY)) 270`
## Warning: Removed 35 row(s) containing missing values (geom_path).
## Warning: Removed 241 rows containing missing values (geom_point).

## 
## $`_VS(Phospho (STY))REFHSHEFHSHEDM(Oxidation (M))LVVDPK_ (Oxidation (M)) 284`
## Warning: Removed 37 row(s) containing missing values (geom_path).
## Warning: Removed 292 rows containing missing values (geom_point).

## 
## $`_S(Phospho (STY))GEATDGARPQALPEPMQESK_ (Phospho (STY)) 142`
## Warning: Removed 12 row(s) containing missing values (geom_path).
## Warning: Removed 72 rows containing missing values (geom_point).

## 
## $`_GILAADESTGS(Phospho (STY))IAK_ (Phospho (STY)) 39`
## Warning: Removed 1 row(s) containing missing values (geom_path).
## Warning: Removed 30 rows containing missing values (geom_point).

LS0tDQp0aXRsZTogInBob3NwaG9EYXRhc2V0IG1zcXJvYlBUTSBmaW5hbCB3b3JrZmxvdyB3aXRoIEdQIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB5ZXMNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAnNCcNCi0tLQ0KDQpgYGB7ciwgc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICJDOi9Vc2Vycy9OaW5hL09uZURyaXZlIC0gVUdlbnQvRG9jdW1lbnRlbi9Eb2N0b3JhYXQvbXNxcm9iUFRNIHBhcGVyL2Jpb2xvZ2ljYWwgcGhvc3BobyBkYXRhc2V0LyIpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoUUZlYXR1cmVzKQ0KbGlicmFyeShtc3Fyb2IyKQ0KbGlicmFyeSh0aWJibGUpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ3QpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoc3RhZ2VSKQ0KbGlicmFyeShwb29scikNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShzZXFpbnIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KEV4cGxvcmVNb2RlbE1hdHJpeCkNCmxpYnJhcnkoZGF0YS50YWJsZSkNCmBgYA0KDQojIEltcG9ydCBkYXRhDQoNCg0KYGBge3J9DQpkZiA8LSByZWFkLmNzdigiZXZpZGVuY2UudHh0IixoZWFkZXI9VFJVRSwgc2VwID0gIiwiKQ0KI0kgY2FuIGlnbm9yZSB0aGUgc2lnbWEgcHJvdGVpbnMgKHRob3NlIHdlcmUganVzdCB1c2VkIGZvciBRQyBwdXJwb3NlcykNCmRmIDwtIGRmICU+JSBmaWx0ZXIoIWdyZXBsKCJTaWdtYSIsIGRmJFByb3RlaW5zKSkNCiNmaWx0ZXIgb3V0IHBob3NwaG8gc2l0ZXMgd2l0aCBsZXNzIHRoYW4gNzUlIHByb2JhYmlsaXR5DQpmaWx0ZXJfcm93cyA8LSBzYXBwbHkoZGYkUGhvc3Boby4uU1RZLi5Qcm9iYWJpbGl0aWVzLCBmdW5jdGlvbih4KXsNCiAgaWYgKCF4PT0iIil7DQogICAgcHJvYiA8LSBzdHJfZXh0cmFjdF9hbGwoeCwgICIoPzw9XFwoKS4rPyg/PVxcKSkiKVtbMV1dDQogICAgaWYgKCFhbnkoYXMuZG91YmxlKHByb2IpID4gMC43NSkpe3JldHVybih3aGljaChkZiRQaG9zcGhvLi5TVFkuLlByb2JhYmlsaXRpZXMgPT0geCkpfQ0KICB9DQp9LCBVU0UuTkFNRVMgPSBGKQ0KZmlsdGVyX3Jvd3MgPC0gdW5pcXVlKHVubGlzdChmaWx0ZXJfcm93cykpDQpucm93KGRmKQ0KZGYgPC0gZGZbLWZpbHRlcl9yb3dzLF0NCm5yb3coZGYpDQojTm93IGdldCBmb3JtYXQgaW50byB3aWRlIGZvcm1hdA0KZGZfd2lkZSA8LSBwaXZvdF93aWRlcihkZiwgaWRfY29scyA9IGMoIlNlcXVlbmNlIiwgIk1vZGlmaWNhdGlvbnMiLCAiTW9kaWZpZWQuc2VxdWVuY2UiLCAiUHJvdGVpbnMiLCAiTGVhZGluZy5wcm90ZWlucyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJldmVyc2UiLCAiUG90ZW50aWFsLmNvbnRhbWluYW50IiwgIlByb3RlaW4uZ3JvdXAuSURzIiwgIkxlYWRpbmcucmF6b3IucHJvdGVpbiIpLA0KICAgICAgICAgICAgICAgICAgICAgICBuYW1lc19mcm9tID0gIlJhdy5maWxlIiwgbmFtZXNfcHJlZml4ID0gIkludGVuc2l0eV8iLCB2YWx1ZXNfZnJvbSA9ICJJbnRlbnNpdHkiLCB2YWx1ZXNfZm4gPSBtYXgpDQpgYGANCg0KYGBge3J9DQplY29scyA8LSBncmVwKCJJbnRlbnNpdHkiLGNvbG5hbWVzKGRmX3dpZGUpKQ0KI29yZGVyIGRhdGFmcmFtZSBieSBwcm90ZWluLCBmb3IgdGhlIG5vcm1hbGlzYXRpb24gc3RlcA0KZGZfd2lkZSA9IGRmX3dpZGVbb3JkZXIoZGZfd2lkZSRMZWFkaW5nLnJhem9yLnByb3RlaW4pLF0NCnBlIDwtIHJlYWRRRmVhdHVyZXMoZGZfd2lkZSxlY29sPSBlY29scyxuYW1lPSJwZXB0aWRvZm9ybVJhdyIpDQpgYGANCg0KYGBge3J9DQpyb3duYW1lcyhwZVtbInBlcHRpZG9mb3JtUmF3Il1dKSA8LSByb3dEYXRhKHBlW1sicGVwdGlkb2Zvcm1SYXciXV0pJE1vZGlmaWVkLnNlcXVlbmNlDQpgYGANCg0KIyMgRXhwZXJpbWVudGFsIExheW91dA0KDQpgYGB7cn0NCm1ldGFkYXRhIDwtIHJlYWQuY3N2KCJFeHBlcmltZW50YWwgRGVzaWduLmNzdiIpDQpgYGANCg0KYGBge3J9DQpjb2xEYXRhKHBlKSRmaWxlIDwtIHNhcHBseShzdHJfc3BsaXQocm93bmFtZXMoY29sRGF0YShwZSkpLCAiXyIpLCBmdW5jdGlvbih4KSB4W1syXV0pDQptZXRhZGF0YSRGaWxlIDwtIHN1Yih4PW1ldGFkYXRhJEZpbGUsIHBhdHRlcm49Ii5yYXciLHJlcGxhY2VtZW50ID0gIiIpDQpjb2xEYXRhKHBlKSRjb25kaXRpb24gPC0gc2FwcGx5KGNvbERhdGEocGUpJGZpbGUsIGZ1bmN0aW9uKHgpew0KICBtZXRhZGF0YVttZXRhZGF0YSRGaWxlPT14LF0kQ29uZGl0aW9uDQp9LCBVU0UuTkFNRVMgPSBGKQ0KY29sRGF0YShwZSkkc3Vic2V0IDwtIHNhcHBseShjb2xEYXRhKHBlKSRmaWxlLCBmdW5jdGlvbih4KXsNCiAgbWV0YWRhdGFbbWV0YWRhdGEkRmlsZT09eCxdJFN1YnNldA0KfSwgVVNFLk5BTUVTID0gRikNCmNvbERhdGEocGUpJGNvbmRpdGlvbiA8LSBjYXNlX3doZW4oZ3JlcGwoIkEiLCBjb2xEYXRhKHBlKSRjb25kaXRpb24pIH4gIkEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiQiIpDQpjb2xEYXRhKHBlKQ0KYGBgDQoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCk1TbmJhc2U6OnBsb3ROQShhc3NheShwZVtbInBlcHRpZG9mb3JtUmF3Il1dKSkgKw0KICB4bGFiKCJQZXB0aWRlIGluZGV4IChvcmRlcmVkIGJ5IGRhdGEgY29tcGxldGVuZXNzKSIpDQpgYGANCg0KIyMgUHJvdGVpbiBkYXRhc2V0DQoNCmBgYHtyfQ0KZGZfcHJvdCA8LSByZWFkLmNzdigiTm9uLWVucmljaGVkL2V2aWRlbmNlLnR4dCIsaGVhZGVyPVRSVUUsIHNlcCA9ICIsIikNCiNJIGNhbiBpZ25vcmUgdGhlIHBvb2wgc2FtcGxlcyAodGhvc2Ugd2VyZSBqdXN0IHVzZWQgZm9yIFFDIHB1cnBvc2VzKQ0KIzIgc2FtcGxlcyBsYWNrIG1ldGFkYXRhIGluZm8sIHNvIEkgd2lsbCByZW1vdmUgdGhlbSBmb3Igbm93IGFzIHdlbGwNCmRmX3Byb3QgPC0gZGZfcHJvdCAlPiUgZmlsdGVyKCFncmVwbCgiU2lnbWEiLCBkZl9wcm90JFByb3RlaW5zKSwNCiAgICAgICAgICAgICAgICAgICAgI2V4dHJhIGZpbGVzIG5vdCBwcmVzZW50IGluIHRoZSBwdG0gZGF0YXNldCwgc28gbm90IHVzZWZ1bA0KICAgICAgICAgICAgICAgICAgICAhZ3JlcGwoIlFFMzc2NTZHTXxRRTM3NzQ5R018UUUzNzc2OUdNfFFFMzc4MTFHTXxRRTM3OTEzR018UUUzNzY5OEdNfFFFMzc3ODFHTXxRRTM3OTE5R018UUUzNzg5OEdNfFFFMzc5NzlHTXxRRTM3NzEzR00iLCBkZl9wcm90JFJhdy5maWxlKSkNCg0KI05vdyBnZXQgZm9ybWF0IGludG8gd2lkZSBmb3JtYXQNCmRmX3Byb3Rfd2lkZSA8LSBwaXZvdF93aWRlcihkZl9wcm90LCBpZF9jb2xzID0gYygiU2VxdWVuY2UiLCAiTW9kaWZpY2F0aW9ucyIsICJNb2RpZmllZC5zZXF1ZW5jZSIsICJQcm90ZWlucyIsICJMZWFkaW5nLnByb3RlaW5zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmV2ZXJzZSIsICJQb3RlbnRpYWwuY29udGFtaW5hbnQiLCAiUHJvdGVpbi5ncm91cC5JRHMiLCAiTGVhZGluZy5yYXpvci5wcm90ZWluIiksDQogICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSAiUmF3LmZpbGUiLCBuYW1lc19wcmVmaXggPSAiSW50ZW5zaXR5XyIsIHZhbHVlc19mcm9tID0gIkludGVuc2l0eSIsIHZhbHVlc19mbiA9IG1heCkNCmBgYA0KDQpgYGB7cn0NCmVjb2xzIDwtIGdyZXAoIkludGVuc2l0eSIsY29sbmFtZXMoZGZfcHJvdF93aWRlKSkNCiNvcmRlciBkYXRhZnJhbWUgYnkgcHJvdGVpbiwgZm9yIHRoZSBub3JtYWxpc2F0aW9uIHN0ZXANCmRmX3Byb3Rfd2lkZSA9IGRmX3Byb3Rfd2lkZVtvcmRlcihkZl9wcm90X3dpZGUkTGVhZGluZy5yYXpvci5wcm90ZWluKSxdDQpwcm90IDwtIHJlYWRRRmVhdHVyZXMoZGZfcHJvdF93aWRlLGVjb2w9IGVjb2xzLG5hbWU9InByb3RlaW5SYXciKQ0Kcm93bmFtZXMocHJvdFtbInByb3RlaW5SYXciXV0pIDwtIHJvd0RhdGEocHJvdFtbInByb3RlaW5SYXciXV0pJE1vZGlmaWVkLnNlcXVlbmNlDQpgYGANCg0KVG90YWwgcHJvdGVpbiBvdmVybGFwDQoNCmBgYHtyfQ0KcHJvdF9HUCA8LSB1bmlxdWUoZGZfcHJvdF93aWRlJExlYWRpbmcucmF6b3IucHJvdGVpbikNCnByb3RfUCA8LSB1bmlxdWUoZGZfd2lkZSRMZWFkaW5nLnJhem9yLnByb3RlaW4pDQojaG93IG1hbnkgb2YgdGhlIHByb3RlaW4gaW4gZW5yaWNoZWQgZGF0YXNldCBhcmUgbm90IGluIG5vbi1lbnJpY2hlZCBkYXRhc2V0Pw0Kbl9kaWZmIDwtIGxlbmd0aChzZXRkaWZmKHByb3RfUCwgcHJvdF9HUCkpDQpuX2RpZmYvbGVuZ3RoKHByb3RfUCkNCmBgYA0KDQoyNSUgb2YgcHJvdGVpbnMgaW4gdGhlIGVucmljaGVkIGRhdGFzZXQgZG8gbm90IGZpbmQgYSBjb3VudGVycGFydCBpbiB0aGUgbm9uLWVucmljaGVkIGRhdGFzZXQNCg0KIyMjIEV4cGVyaW1lbnRhbCBMYXlvdXQNCg0KYGBge3J9DQptZXRhZGF0YSA8LSByZWFkLmNzdigiTm9uLWVucmljaGVkL21ldGFkYXRhLmNzdiIpDQpgYGANCg0KYGBge3J9DQpjb2xEYXRhKHByb3QpJGZpbGUgPC0gc2FwcGx5KHN0cl9zcGxpdChyb3duYW1lcyhjb2xEYXRhKHByb3QpKSwgIl8iKSwgZnVuY3Rpb24oeCkgeFtbMl1dKQ0KbWV0YWRhdGEkRmlsZSA8LSBzYXBwbHkoc3Ryc3BsaXQobWV0YWRhdGEkRmlsZSwgIi4iLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB4W1sxXV0pDQptZXRhZGF0YSA8LSBtZXRhZGF0YSAlPiUgZmlsdGVyKCFncmVwbCgiUUUzNzY1NkdNfFFFMzc3NDlHTXxRRTM3NzY5R018UUUzNzgxMUdNfFFFMzc5MTNHTXxRRTM3Njk4R018UUUzNzc4MUdNfFFFMzc5MTlHTXxRRTM3ODk4R018UUUzNzk3OUdNfFFFMzc3MTNHTSIsIG1ldGFkYXRhJEZpbGUpKQ0KY29sRGF0YShwcm90KSRjb25kaXRpb24gPC0gc2FwcGx5KGNvbERhdGEocHJvdCkkZmlsZSwgZnVuY3Rpb24oeCl7DQogIG1ldGFkYXRhW21ldGFkYXRhJEZpbGU9PXgsXSRDb25kaXRpb24NCn0sIFVTRS5OQU1FUyA9IEYpDQpjb2xEYXRhKHByb3QpJHN1YnNldCA8LSBzYXBwbHkoY29sRGF0YShwcm90KSRmaWxlLCBmdW5jdGlvbih4KXsNCiAgbWV0YWRhdGFbbWV0YWRhdGEkRmlsZT09eCxdJFN1YnNldA0KfSwgVVNFLk5BTUVTID0gRikNCmNvbERhdGEocHJvdCkkY29uZGl0aW9uIDwtIGNhc2Vfd2hlbihncmVwbCgiQSIsIGNvbERhdGEocHJvdCkkY29uZGl0aW9uKSB+ICJBIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIkIiKQ0KY29sRGF0YShwcm90KQ0KYGBgDQoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCk1TbmJhc2U6OnBsb3ROQShhc3NheShwcm90W1sicHJvdGVpblJhdyJdXSkpICsNCiAgeGxhYigiUGVwdGlkZSBpbmRleCAob3JkZXJlZCBieSBkYXRhIGNvbXBsZXRlbmVzcykiKQ0KYGBgDQoNCiMgUHJlcHJvY2Vzc2luZw0KDQojIyBQVE0gZGF0YXNldA0KDQoNCg0KYGBge3J9DQpyb3dEYXRhKHBlW1sicGVwdGlkb2Zvcm1SYXciXV0pJG5Ob25aZXJvIDwtIHJvd1N1bXMoYXNzYXkocGVbWyJwZXB0aWRvZm9ybVJhdyJdXSkgPiAwLCBuYS5ybSA9IFQpDQpwZSA8LSB6ZXJvSXNOQShwZSwgaSA9ICJwZXB0aWRvZm9ybVJhdyIpDQpwZSA8LSBmaWx0ZXJGZWF0dXJlcyhwZSwgflJldmVyc2UgIT0gIisiKQ0KcGUgPC0gZmlsdGVyRmVhdHVyZXMocGUsIH5Qb3RlbnRpYWwuY29udGFtaW5hbnQgIT0gIisiKQ0KcGUgPC0gbG9nVHJhbnNmb3JtKHBlLCBiYXNlID0gMiwgaSA9ICJwZXB0aWRvZm9ybVJhdyIsIG5hbWUgPSAicGVwdGlkb2Zvcm1Mb2ciKQ0KcGUgPC0gcGVbcm93RGF0YShwZVtbInBlcHRpZG9mb3JtUmF3Il1dKSRuTm9uWmVybz4yLCxdDQpwZSA8LSBRRmVhdHVyZXM6Om5vcm1hbGl6ZShwZSwgbWV0aG9kID0gImNlbnRlci5tZWRpYW4iLCBpID0gInBlcHRpZG9mb3JtTG9nIiwgbmFtZSA9ICJwZXB0aWRvZm9ybSIpDQpjb2xEYXRhKHBlW1sicGVwdGlkb2Zvcm0iXV0pIDwtIGNvbERhdGEocGUpDQpgYGANCg0KDQoNCiMjIFByb3RlaW4gZGF0YXNldA0KDQpgYGB7cn0NCnJvd0RhdGEocHJvdFtbInByb3RlaW5SYXciXV0pJG5Ob25aZXJvIDwtIHJvd1N1bXMoYXNzYXkocHJvdFtbInByb3RlaW5SYXciXV0pID4gMCwgbmEucm0gPSBUKQ0KcHJvdCA8LSB6ZXJvSXNOQShwcm90LCBpID0gInByb3RlaW5SYXciKQ0KcHJvdCA8LSBsb2dUcmFuc2Zvcm0ocHJvdCwgYmFzZSA9IDIsIGkgPSAicHJvdGVpblJhdyIsIG5hbWUgPSAicHJvdGVpbkxvZyIpDQpwcm90IDwtIHByb3Rbcm93RGF0YShwcm90W1sicHJvdGVpblJhdyJdXSkkbk5vblplcm8+MiwsXQ0KcHJvdCA8LSBRRmVhdHVyZXM6Om5vcm1hbGl6ZShwcm90LCBtZXRob2QgPSAiY2VudGVyLm1lZGlhbiIsIGkgPSAicHJvdGVpbkxvZyIsIG5hbWUgPSAicHJvdGVpbiIpDQpjb2xEYXRhKHByb3RbWyJwcm90ZWluIl1dKSA8LSBjb2xEYXRhKHByb3QpDQpgYGANCg0KDQojIyBOb3JtYWxpc2F0aW9uIHZpYSByb2J1c3Qgc3VtbWFyaXNhdGlvbg0KDQoNCmBgYHtyLHdhcm5pbmc9RkFMU0V9DQpwcm90IDwtIGFnZ3JlZ2F0ZUZlYXR1cmVzKHByb3QsDQogaSA9ICJwcm90ZWluIiwNCiBmY29sID0gIkxlYWRpbmcucmF6b3IucHJvdGVpbiIsDQogbmEucm0gPSBUUlVFLA0KIG5hbWUgPSAicHJvdGVpblJvYnVzdCIsDQogZnVuID0gTXNDb3JlVXRpbHM6OnJvYnVzdFN1bW1hcnkpDQpgYGANCg0KSSBoYXZlIHRvIGNoYW5nZSB0aGUgY29sbmFtZXMgb2YgdGhlIHByb3RlaW5Sb2J1c3QgYXNzYXkgc28gdGhhdCBpdCBjYW4gYmUgdXNlZCBhcyBhIGNlbnRlcmluZyBmb3IgdGhlIHB0bSBkYXRhc2V0LiBJIGhhdmUgbWF0Y2hlZCB0aGUgY29ycmVzcG9uZGluZyBzYW1wbGVzIHRvIGVhY2ggb3RoZXIgdmlhIHRoZSBtZXRhZGF0YSBmaWxlcyAoY29ycmVzcG9uZGFuY2UvY29uZGl0aW9uIGNvbHVtbikgYW5kIGp1c3QgY2hhbmdlZCB0aGUgcmF3IGZpbGVzIG5hbWVzIHRvIHRoZSByYXcgZmlsZXMgbmFtZXMgb2YgdGhlIHB0bSBkYXRhc2V0LiBTZWUgYWxzbyByYXdmaWxlbmFtZXNfY2hhbmdlLmNzdg0KDQpgYGB7cn0NCnJhd2ZpbGVuYW1lcyA8LSByZWFkLmNzdigicmF3ZmlsZW5hbWVzX2NoYW5nZS5jc3YiKQ0KI2dldCBjdXJyZW50IHByb3RlaW4gY29sbmFtZXMgd2l0aG91dCAiSW50ZW5zaXR5XyINCmN1cnJlbnRfY29sbmFtZXMgPC0gc2FwcGx5KHN0cnNwbGl0KGNvbG5hbWVzKGFzc2F5KHByb3RbWyJwcm90ZWluUm9idXN0Il1dKSksICJfIiksIGZ1bmN0aW9uKHgpIHhbWzJdXSkNCiNyZXBsYWNlIHdpdGggY29ycmVzcG9uZGluZyBwdG0gY29sbmFtZXMNCmNvbG5hbWVzKGFzc2F5KHByb3RbWyJwcm90ZWluUm9idXN0Il1dLCB3aXRoRGltbmFtZXMgPSBGKSkgPC0gDQogIHNhcHBseShjdXJyZW50X2NvbG5hbWVzLCBmdW5jdGlvbih4KXsNCiAgICAgICAgICAgICAgICAgIHB0bV9jb2xuYW1lIDwtIHJhd2ZpbGVuYW1lc1tyYXdmaWxlbmFtZXNbWyJwcm90ZWluLmRhdGFzZXQiXV09PXgsXSRgcHRtLmRhdGFzZXRgDQogICAgICAgICAgICAgICAgICBwYXN0ZTAoIkludGVuc2l0eV8iLCBwdG1fY29sbmFtZSkNCiAgICAgICAgICAgICAgICB9LCBVU0UuTkFNRVMgPSBGKQ0KYGBgDQoNCmBgYHtyfQ0KI09ubHkgdGFrZSBwZXBmb3JtcyB0aGF0IGhhdmUgYSBwYXJlbnQgcHJvdGVpbg0KIyh3aGVuIHRoZXJlIGlzIG5vIGdsb2JhbCBwcm9maWxpbmcgZGF0YXNldCwgdGhpcyB3aWxsIGJlIGFsbCBwZXB0aWRvZm9ybXMpDQpwZXBXaXRoUHJvdGVpbiA8LSByb3dEYXRhKHBlW1sicGVwdGlkb2Zvcm0iXV0pJFByb3RlaW5zICVpbiUgcm93bmFtZXMocHJvdFtbInByb3RlaW5Sb2J1c3QiXV0pDQpwZVBlcFdpdGhQcm90ZWluICA8LSBwZVtbInBlcHRpZG9mb3JtIl1dW3BlcFdpdGhQcm90ZWluLF0NCnBlIDwtIGFkZEFzc2F5KHBlLHBlUGVwV2l0aFByb3RlaW4sInBlcGZvcm1SZWwiKQ0KI25vcm1hbGlzYXRpb24gZm9yIHByb3RlaW4gYWJ1bmRhbmNlIHN0ZXANCmFzc2F5KHBlW1sicGVwZm9ybVJlbCJdXSkgPC0gYXNzYXkocGVbWyJwZXBmb3JtUmVsIl1dKSAtIGFzc2F5KHByb3RbWyJwcm90ZWluUm9idXN0Il1dLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpdGhEaW1uYW1lcyA9IEYpW3Jvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSRQcm90ZWlucyxjb2xuYW1lcyhhc3NheShwZVtbInBlcGZvcm1SZWwiXV0pKV0NCmJveHBsb3QoYXNzYXkocGVbWyJwZXBmb3JtUmVsIl1dKSkNCmBgYA0KDQoNCg0KDQojIERpZmZlcmVudGlhbCBwZXB0aWRvZm9ybSB1c2FnZSAoRFBGVSkNCg0KIyMgSHlwb3RoZXNpc3Rlc3QgZm9yIGVhY2ggY29udHJhc3QNCg0KY29uZGl0aW9uIEEgdnMgQg0KDQpgYGB7cn0NCmNvbERhdGEocGUpJGNvbmRpdGlvbiA8LSByZWxldmVsKGFzLmZhY3Rvcihjb2xEYXRhKHBlKSRjb25kaXRpb24pLCByZWYgPSAiQiIpDQpjb2xEYXRhKHBlKSRzdWJzZXQgPC0gYXMuZmFjdG9yKGNvbERhdGEocGUpJHN1YnNldCkNCmNvbERhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSA8LSBjb2xEYXRhKHBlKQ0KYGBgDQoNCg0KYGBge3J9DQpwZSA8LSBtc3Fyb2IyOjptc3Fyb2Iob2JqZWN0ID0gcGUsIGkgPSAicGVwZm9ybVJlbCIsIGZvcm11bGEgPSB+Y29uZGl0aW9uKnN1YnNldCwgcm9idXN0PUZBTFNFKQ0Kcm93RGF0YShwZVtbInBlcGZvcm1SZWwiXV0pJG1zcXJvYk1vZGVsc1tbMl1dICU+JQ0KICAgICAgICAgICAgICAgICAgZ2V0Q29lZg0KYGBgDQoNCg0KYGBge3J9DQpmcmFjdGlvbl9vZl95cyA8LSAoY29sRGF0YShwZSkgJT4lIGFzX3RpYmJsZSgpICU+JSBmaWx0ZXIoc3Vic2V0ID09ICJ5IikgJT4lIG5yb3coKSkgLyBucm93KGNvbERhdGEocGUpKQ0KY29udHJhc3RzIDwtIGMoImNvbmRpdGlvbkEiLCAiY29uZGl0aW9uQSArIGNvbmRpdGlvbkE6c3Vic2V0eSIsICJjb25kaXRpb25BOnN1YnNldHkiLCAiY29uZGl0aW9uQSArIDAuNSAqIGNvbmRpdGlvbkE6c3Vic2V0eSIsICJjb25kaXRpb25BICsgMC42NjY2NjY3ICogY29uZGl0aW9uQTpzdWJzZXR5IikNCkwgPC0gbWFrZUNvbnRyYXN0KGMoImNvbmRpdGlvbkEgPSAwIiwNCiAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbkEgKyBjb25kaXRpb25BOnN1YnNldHkgPSAwIiwNCiAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbkE6c3Vic2V0eSA9IDAiLA0KICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uQSArIDAuNSAqIGNvbmRpdGlvbkE6c3Vic2V0eSA9IDAiLA0KICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uQSArIDAuNjY2NjY2NyAqIGNvbmRpdGlvbkE6c3Vic2V0eSA9IDAiKSwNCiAgICAgICAgICAgICAgICAgIHBhcmFtZXRlck5hbWVzID0gcm93RGF0YShwZVtbInBlcGZvcm1SZWwiXV0pJG1zcXJvYk1vZGVsc1tbMl1dICU+JQ0KICAgICAgICAgICAgICAgICAgZ2V0Q29lZiAlPiUNCiAgICAgICAgICAgICAgICAgIG5hbWVzKQ0KcGUgPC0gaHlwb3RoZXNpc1Rlc3Qob2JqZWN0ID0gcGUsIGkgPSAicGVwZm9ybVJlbCIsIGNvbnRyYXN0ID0gTCkNCmBgYA0KDQoNCiMjIyBWb2xjYW5vcGxvdCANCg0KYGBge3J9DQp2b2xjYW5vIDwtIGxpc3QoKQ0KZm9yIChjb250cmFzdCBpbiBjb250cmFzdHMpew0Kdm9sY2Fub1tbY29udHJhc3RdXSA8LSByb3dEYXRhKHBlW1sicGVwZm9ybVJlbCJdXSlbW2NvbnRyYXN0XV0lPiUNCiAgICAgICAgICAgIGdncGxvdChhZXMoeCA9IGxvZ0ZDLCB5ID0gLWxvZzEwKHB2YWwpLCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGFkalB2YWwgPCAwLjA1LA0KICAgICAgICAgICAgICAgICAgIGFubm90YXRpb249cm93RGF0YShwZVtbInBlcGZvcm1SZWwiXV0pWywzXSkpICsNCiAgZ2VvbV9wb2ludChjZXggPSAyLjUpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGFscGhhKGMoImJsYWNrIiwgInJlZCIpLCAwLjUpKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB5bGFiKCItbG9nMTAocHZhbHVlKSIpICsNCiAgZ2d0aXRsZShjb250cmFzdCkNCn0NCnZvbGNhbm8NCmBgYA0KDQoNCiMjIyBTaWduaWZpY2FudCBwZXB0aWRvZm9ybXMgZm9yIGVhY2ggY29udHJhc3QNCg0KDQpgYGB7cn0NCnRhYmxlcyA8LSBsaXN0KCkNCmZvciAoY29udHJhc3QgaW4gY29udHJhc3RzKXsNCnNpZ1RhYmxlIDwtIHJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKVtbY29udHJhc3RdXQ0KaWYobnJvdyhzaWdUYWJsZSA8LSBzaWdUYWJsZSAlPiUNCiAgbmEuZXhjbHVkZSAlPiUNCiAgZmlsdGVyKGFkalB2YWw8MC4wNSkpPjApew0Kc2lnVGFibGUgPC0gc2lnVGFibGUgJT4lDQogIG5hLmV4Y2x1ZGUgJT4lDQogIGZpbHRlcihhZGpQdmFsPDAuMDUpICU+JQ0KICBhcnJhbmdlKHB2YWwpICU+JQ0KICBtdXRhdGUoDQogICAgc2UgPSBmb3JtYXQoc2UsIGRpZ2l0cyA9IDIpLCANCiAgICBkZiA9IGZvcm1hdChkZiwgZGlnaXRzID0yKSwNCiAgICB0ID0gZm9ybWF0KHQsIGRpZ2l0cyA9IDIpLA0KICAgIGFkalB2YWwgPSBmb3JtYXQoYWRqUHZhbCwgZGlnaXRzID0gMyksDQogICAgcmFuayA9IDE6bGVuZ3RoKGxvZ0ZDKSANCiAgKSANCnNpZ1RhYmxlX3ByaW50IDwtIHNpZ1RhYmxlICU+JSBtdXRhdGUocGVwdGlkb2Zvcm0gPSByb3duYW1lcyhzaWdUYWJsZSkpICU+JSBndCgpICU+JSB0YWJfaGVhZGVyKHRpdGxlID0gbWQoY29udHJhc3QpKQ0KdGFibGVzW1tjb250cmFzdF1dIDwtIHNpZ1RhYmxlDQoja25pdHI6OmthYmxlKHNpZ1RhYmxlLCBjYXB0aW9uID0gY29udHJhc3QpDQp9DQp9DQprbml0cjo6a2FibGUodGFibGVzKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQp0YWJsZXNfdG9Dc3YgPC0gZG8uY2FsbCgicmJpbmQiLCB0YWJsZXMpDQp0YWJsZXNfdG9Dc3YgPC0gdGFibGVzX3RvQ3N2ICU+JSByb3duYW1lc190b19jb2x1bW4oImNvbnRyYXN0IikNCndyaXRlLmNzdih0YWJsZXNfdG9Dc3YsIGZpbGUgPSAic2lnbmlmaWNhbmNlX3RhYmxlc19HUF9wZXBmb3JtLmNzdiIpDQpgYGANCg0KIyBEUFRNDQoNCiMjIHB0bSBzdW1tYXJpc2F0aWUNCg0KU3VtbWFyaXNlIHBlcHRpZG9mb3JtcyB0byBwdG0gbGV2ZWwNCg0KIyMgRGV0ZXJtaW5lIGxvY2F0aW9uIG9mIHRoZSBtb2RpZmljYXRpb24NCg0KYGBge3J9DQpmYXN0YSA8LSAiaHVtYW4uZmFzdGEiDQpwYXJzZWRfZmFzdGEgPC0gcmVhZC5mYXN0YShmaWxlID0gZmFzdGEsIHNlcXR5cGUgPSAiQUEiLCBhcy5zdHJpbmcgPSBUKQ0KYGBgDQoNCmBgYHtyfQ0KI21vZGlmaWVkIHNlcXVlbmNlIGNvbHVtbiBjb250YWlucyBfIHRoYXQgZG9lcyBub3QgbWF0dGVyLCBidXQgaGluZGVycyBsb2NhdGlvbiBkZXRlcm1pbmF0aW9uDQpyb3dEYXRhKHBlW1sicGVwZm9ybVJlbCJdXSkkbW9kaWZpZWRfc2VxdWVuY2UgPC0gZ3N1YigiXyIsICIiLCByb3dEYXRhKHBlW1sicGVwZm9ybVJlbCJdXSkkTW9kaWZpZWQuc2VxdWVuY2UpDQpgYGANCg0KICANCg0KYGBge3J9DQpnZXRfcHRtX2xvY2F0aW9uIDwtIGZ1bmN0aW9uKGZlYXR1cmUsIGRhdGEsIGZhc3RhLCBtb2RfY29sdW1uID0gIk1vZGlmaWNhdGlvbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVwdGlkZV9zZXFfY29sdW1uID0gIlNlcXVlbmNlIiwgbW9kX3NlcV9jb2x1bW4gPSAibW9kaWZpZWRfc2VxdWVuY2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvdGVpbl9jb2x1bW4gPSAiTGVhZGluZy5yYXpvci5wcm90ZWluIiwgc3BsaXQgPSAiLCIsIGNvbGxhcHNlID0gIiwgIil7DQogIHByb3QgPC0gZGF0YVtmZWF0dXJlLF1bW3Byb3RlaW5fY29sdW1uXV0NCiAgcGVwX3NlcSA8LSBkYXRhW2ZlYXR1cmUsXVtbcGVwdGlkZV9zZXFfY29sdW1uXV0NCiAgbW9kX3NlcSA8LSBkYXRhW2ZlYXR1cmUsXVtbbW9kX3NlcV9jb2x1bW5dXQ0KICBwcm90X3NlcSA8LSBmYXN0YVtbcHJvdF1dWzFdDQoNCiAgI2ZpbmQgbG9jYXRpb24gb2YgcGVwdGlkZSBpbiBwcm90ZWluDQogICNhZGQgLTEgaGVyZSwgc28gdGhhdCB0aGUgYWRkaXRpb24gb2YgdGhlIGxvY2F0aW9uIGxhdGVyIG9uIGlzIGNvcnJlY3QNCiAgcGVwX2xvY2F0aW9uIDwtIHVubGlzdChsYXBwbHkoZ3JlZ2V4cHIocGF0dGVybiA9IHBlcF9zZXEsIHByb3Rfc2VxKSwgbWluKSkgLSAxDQogIGZpbmFsX21vZCA8LSBjKCkNCiAgaiA8LSBtb2Rfc2VxDQogICNnbyBvdmVyIHRoZSBtb2RpZmljYXRpb25zIGluc2lkZSB0aGUgbW9kaWZpZWQgc2VxdWVuY2UNCiAgZm9yKG1vZCBpbiByZWdtYXRjaGVzKG1vZF9zZXEsIGdyZWdleHByKCJcXCguKj9cXClcXCkiLCBtb2Rfc2VxLCBwZXJsPVQpKVtbMV1dKXsNCiAgICAjZmluZCBsb2NhdGlvbiBvZiBtb2RpZmljYXRpb24gaW4gcGVwdGlkZQ0KICAgIG1vZF9sb2NhdGlvbiA8LSB1bmxpc3QobGFwcGx5KGdyZWdleHByKHBhdHRlcm4gPSBtb2QsIGosIGZpeGVkID0gVCksIG1pbikpDQogICAgI2dldCBsb2NhdGlvbiBpbiBwcm90ZWluICgtMSBiZWNhdXNlIGVsc2UgaXQgZ2l2ZXMgeW91IHRoZSBsb2NhdGlvbiBhZnRlciBiZWNhdXNlIG9mIHRoZSBwcmVzZW5jZSBvZiB0aGUgbW9kaWZpY2F0aW9uIGluIHRoZSBzdHJpbmcpDQogICAgbG9jYXRpb24gPC0gbW9kX2xvY2F0aW9uICsgcGVwX2xvY2F0aW9uIC0xDQogICAgI2FkZCBsb2NhdGlvbiB0byBtb2RpZmljYXRpb24NCiAgICBtb2RfIDwtIHBhc3RlKG1vZCwgbG9jYXRpb24pDQogICAgI3NhdmUgbW9kaWZpY2F0aW9uDQogICAgZmluYWxfbW9kIDwtIGMoZmluYWxfbW9kLCBtb2RfKQ0KICAgICNub3cgcmVtb3ZlIGN1cnJlbnQgbW9kaWZpY2F0aW9uIGZyb20gdGhlIHNlcXVlbmNlLCBzbyB0aGF0IHdlIGNhbiBjb250aW51ZSB0byB0aGUgbmV4dCBtb2QNCiAgICBzdHJfc3ViKGosIG1vZF9sb2NhdGlvbiwgbmNoYXIobW9kKSttb2RfbG9jYXRpb24tMSkgPC0gIiINCiAgfQ0KICByZXR1cm4ocGFzdGUoZmluYWxfbW9kLCBjb2xsYXBzZSA9IGNvbGxhcHNlKSkNCn0NCmBgYA0KDQpgYGB7cn0NCnJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSRtb2QgPC0gc2FwcGx5KHJvd25hbWVzKHJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSksIGZ1bmN0aW9uKHgpew0KICAgIGdldF9wdG1fbG9jYXRpb24oeCwgcm93RGF0YShwZVtbInBlcGZvcm1SZWwiXV0pLCBwYXJzZWRfZmFzdGEpDQp9LCBVU0UuTkFNRVMgPSBGKQ0KYGBgDQoNCg0KYGBge3J9DQojQWRkIHB0bSB2YXJpYWJsZSA9IHByb3RlaW4gKyBtb2RpZmljYXRpb24NCnJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSRwdG0gPC0gaWZlbHNlKHJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSRtb2QgIT0gIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKHJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSRMZWFkaW5nLnJhem9yLnByb3RlaW4sIHJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSRtb2QsIHNlcD0iXyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiIpDQpgYGANCg0KIyMjIEdldCBwdG0gbGV2ZWwgaW50ZW5zaXR5IG1hdHJpeA0KDQpHZXQgbWF0cml4IGZvciBEUFRNeCAtPiBnZXQgc3VtbWFyaXNlZCBpbnRlbnNpdHkgdmFsdWVzDQoNCg0KYGBge3J9DQpwcm90cyA8LSB1bmlxdWUocm93RGF0YShwZVtbInBlcGZvcm1SZWwiXV0pJExlYWRpbmcucmF6b3IucHJvdGVpbikNCiNEbyBmb3IgZWFjaCBwcm90ZWluDQpwdG1zIDwtIHNhcHBseShwcm90cywgZnVuY3Rpb24oaSkgew0KICBwZV9zdWIgPC0gcGVbWyJwZXBmb3JtUmVsIl1dW2dyZXBsKGksIHJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKSRMZWFkaW5nLnJhem9yLnByb3RlaW4sIGZpeGVkID0gVCksXQ0KICAjcGVfc3ViIDwtIGZpbHRlckZlYXR1cmVzKHBlLH5ncmVwbChMZWFkaW5nLnJhem9yLnByb3RlaW4scGF0dGVybj1pLGZpeGVkID0gVCkpDQogICNHZXQgYWxsIHVuaXF1ZSBtb2RpZmljYXRpb24gcHJlc2VudCBvbiB0aGF0IHByb3RlaW4NCiAgbW9kcyA8LSB1bmlxdWUodW5saXN0KHN0cnNwbGl0KHJvd0RhdGEocGVfc3ViKSRtb2QsIHNwbGl0ID0gIiwgIiwgZml4ZWQgPSBUUlVFKSkpDQogICNBZGQgcHJvdGVpbiBpbmZvIHRvIG1vZHMNCiAgcHRtIDwtIHBhc3RlKHJlcChpLCBsZW5ndGgobW9kcykpLCBtb2RzKQ0KICAjcmV0dXJuIGFsbCB0aGUgcHJvdGVpbi1tb2RzIGNvbWJpbmF0aW9ucw0KICBwdG0NCn0pDQpwdG1zIDwtIGFzLnZlY3Rvcih1bmxpc3QocHRtcykpDQpgYGANCg0KDQoNCmBgYHtyfQ0KI0ZvciBlYWNoIHB0bSBkbw0KcHRtX3hfYXNzYXkgPC0gc2FwcGx5KHNlcSgxOmxlbmd0aChwdG1zKSksIGZ1bmN0aW9uKGkpeyANCiAgeCA8LSBwdG1zW2ldDQogICNHZXQgY3VycmVudCBwcm90ZWluIGFuZCBtb2QgZnJvbSBwdG0NCiAgcHJvdCA8LSBzdHJfc3BsaXQoeCwgIiAiLCAyKVtbMV1dWzFdDQogIGN1cnJlbnRfcHRtIDwtIHN0cl9zcGxpdCh4LCAiICIsIDIpW1sxXV1bMl0NCiAgI2ZpbHRlciBvbiB0aGF0IHByb3RlaW4gYW5kIG9uIHRoYXQgbW9kIHRvIG9idGFpbiBhbGwgcGVwdGlkb2Zvcm1zIHRoYXQgY29ycmVzcG9uZCB0byB0aGUgcHRtDQogIHBlX3N1YiA8LSBwZVtbInBlcGZvcm1SZWwiXV1bZ3JlcGwocHJvdCwgcm93RGF0YShwZVtbInBlcGZvcm1SZWwiXV0pJExlYWRpbmcucmF6b3IucHJvdGVpbiwgZml4ZWQgPSBUKSxdDQogICNwZV9zdWIgPC0gZmlsdGVyRmVhdHVyZXMocGUsfmdyZXBsKExlYWRpbmcucmF6b3IucHJvdGVpbixwYXR0ZXJuPXByb3QsIGZpeGVkID0gVCkpDQogIHB0bV9zdWIgPC0gcGVfc3ViW2dyZXBsKGN1cnJlbnRfcHRtLCByb3dEYXRhKHBlX3N1YikkbW9kLCBmaXhlZCA9IFQpLF0NCiAgI3B0bV9zdWIgPC0gZmlsdGVyRmVhdHVyZXMocGVfc3ViLH5ncmVwbChtb2QscGF0dGVybj1jdXJyZW50X3B0bSwgZml4ZWQ9VCkpW1sicGVwdGlkb2Zvcm1Ob3JtIl1dDQogICNHZXQgaW50ZW5zaXR5IHZhbHVlcyBvZiB0aG9zZSBwZXB0aWRvZm9ybXMNCiAgeSA8LSBhc3NheShwdG1fc3ViKQ0KICAjQW5kIHN1bW1hcmlzZSB0aGVtIHRvIDEgcm93IG9mIGludGVuc2l0eSB2YWx1ZXM6IDEgdmFsdWUgcGVyIHNhbXBsZSBmb3IgdGhhdCBwdG0NCiAgcHRtX3kgPC0gdHJ5KE1zQ29yZVV0aWxzOjpyb2J1c3RTdW1tYXJ5KHkpLCBzaWxlbnQgPSBUKQ0KICBpZiAoaXMocHRtX3ksICJ0cnktZXJyb3IiKSl7DQogICAgcHRtX3kgPC0gcmVwKE5BLCBuY29sKHkpKX0NCiAgcHRtX3kNCn0pDQojVGhlbiB3ZSBnZXQgdGhlIGludGVuc2l0eSBhc3NheSBvbiBwdG0gbGV2ZWwNCnB0bV94X2Fzc2F5IDwtIHQocHRtX3hfYXNzYXkpDQpyb3duYW1lcyhwdG1feF9hc3NheSkgPC0gcHRtcw0KYGBgDQoNCiMjIyBBZGQgdG8gUUZlYXR1cmVzIG9iamVjdA0KDQpGaWx0ZXIgb3V0IHB0bXMgd2l0aCBhbGwgemVybyBpbnRlbnNpdGllcw0KDQpgYGB7cn0NCnByaW50KHBhc3RlKG5yb3cocHRtX3hfYXNzYXkpLCAicHRtcyBiZWZvcmUgZmlsdGVyaW5nIikpDQpmaWx0ZXJpbmcgPC0gcm93U3VtcyhwdG1feF9hc3NheSAhPSAwLCBuYS5ybT1UUlVFKSA+IDAgDQpwdG1feF9hc3NheSA8LSBwdG1feF9hc3NheVtmaWx0ZXJpbmcsXQ0KcHJpbnQocGFzdGUobnJvdyhwdG1feF9hc3NheSksICJwdG1zIGFmdGVyIGZpbHRlcmluZyIpKQ0KYGBgDQoNCmBgYHtyfQ0KYWxsKHJvd25hbWVzKGNvbERhdGEocGUpKSA9PSBjb2xuYW1lcyhwdG1feF9hc3NheSkpDQpyb3dkYXRhIDwtIGRhdGEuZnJhbWUocHRtID0gcm93bmFtZXMocHRtX3hfYXNzYXkpKQ0Kcm93ZGF0YSRwcm90ZWluIDwtIHNhcHBseShzdHJfc3BsaXQocm93ZGF0YSRwdG0sIHBhdHRlcm49IiAiKSxmdW5jdGlvbih4KSB4WzFdKQ0KcHRtX3lfYXNzYXkgPC0gU3VtbWFyaXplZEV4cGVyaW1lbnQoYXNzYXlzPWFzLm1hdHJpeChwdG1feF9hc3NheSksIHJvd0RhdGE9cm93ZGF0YSwgY29sRGF0YT1jb2xEYXRhKHBlKSkNCmBgYA0KDQoNCmBgYHtyfQ0KcGUgPC0gYWRkQXNzYXkocGUsIHB0bV95X2Fzc2F5LCAicHRtUmVsIikNCmBgYA0KDQoNCg0KIyMgSHlwb3RoZXNpc3Rlc3QgZm9yIGVhY2ggY29udHJhc3QNCg0KYGBge3J9DQpjb2xEYXRhKHBlW1sicHRtUmVsIl1dKQ0KYGBgDQoNCg0KYGBge3J9DQpwZSA8LSBtc3Fyb2IyOjptc3Fyb2Iob2JqZWN0ID0gcGUsIGkgPSAicHRtUmVsIiwgZm9ybXVsYSA9IH5jb25kaXRpb24qc3Vic2V0LCByb2J1c3Q9RkFMU0UsIG92ZXJ3cml0ZSA9IFQpDQpyb3dEYXRhKHBlW1sicHRtUmVsIl1dKSRtc3Fyb2JNb2RlbHNbWzJdXSAlPiUNCiAgICAgICAgICAgICAgICAgIGdldENvZWYNCmBgYA0KDQoNCg0KYGBge3J9DQpjb250cmFzdHMgPC0gYygiY29uZGl0aW9uQSIsICJjb25kaXRpb25BICsgY29uZGl0aW9uQTpzdWJzZXR5IiwgImNvbmRpdGlvbkE6c3Vic2V0eSIsICJjb25kaXRpb25BICsgMC41ICogY29uZGl0aW9uQTpzdWJzZXR5IiwgImNvbmRpdGlvbkEgKyAwLjY2NjY2NjcgKiBjb25kaXRpb25BOnN1YnNldHkiKQ0KTCA8LSBtYWtlQ29udHJhc3QoYygiY29uZGl0aW9uQSA9IDAiLA0KICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uQSArIGNvbmRpdGlvbkE6c3Vic2V0eSA9IDAiLA0KICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uQTpzdWJzZXR5ID0gMCIsDQogICAgICAgICAgICAgICAgICAgICJjb25kaXRpb25BICsgMC41ICogY29uZGl0aW9uQTpzdWJzZXR5ID0gMCIsDQogICAgICAgICAgICAgICAgICAgICJjb25kaXRpb25BICsgMC42NjY2NjY3ICogY29uZGl0aW9uQTpzdWJzZXR5ID0gMCIpLCAgIA0KICAgICAgICAgICAgICAgICAgcGFyYW1ldGVyTmFtZXMgPSByb3dEYXRhKHBlW1sicHRtUmVsIl1dKSRtc3Fyb2JNb2RlbHNbWzNdXSAlPiUNCiAgICAgICAgICAgICAgICAgIGdldENvZWYgJT4lDQogICAgICAgICAgICAgICAgICBuYW1lcykNCnBlIDwtIGh5cG90aGVzaXNUZXN0KG9iamVjdCA9IHBlLCBpID0gInB0bVJlbCIsIGNvbnRyYXN0ID0gTCwgb3ZlcndyaXRlID0gVCkNCmBgYA0KDQoNCg0KIyMjIFZvbGNhbm9wbG90IA0KDQoNCmBgYHtyfQ0Kdm9sY2Fub3MgPC0gbGlzdCgpDQpmb3IgKGNvbnRyYXN0IGluIGNvbnRyYXN0cyl7DQpsaWJyYXJ5KHBsb3RseSkNCnZvbGNhbm9zW1tjb250cmFzdF1dIDwtIA0KICByb3dEYXRhKHBlW1sicHRtUmVsIl1dKVtbY29udHJhc3RdXSU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBsb2dGQywgeSA9IC1sb2cxMChwdmFsKSwgDQogICAgICAgICAgICAgY29sb3IgPSBhZGpQdmFsIDwgMC4wNSwNCiAgICAgICAgICAgICBhbm5vdGF0aW9uPXJvd0RhdGEocGVbWyJwdG1SZWwiXV0pWywzXSkpICsNCiAgZ2VvbV9wb2ludChjZXggPSAyLjUpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGFscGhhKGMoImJsYWNrIiwgInJlZCIpLCAwLjUpKSArIHRoZW1lX21pbmltYWwoKSArDQogIHlsYWIoIi1sb2cxMChwdmFsdWUpIikgKw0KICBnZ3RpdGxlKGNvbnRyYXN0KQ0KfQ0Kdm9sY2Fub3MNCmBgYA0KDQoNCiMjIyBTaWduaWZpY2FudCBwdG1zIGZvciBlYWNoIGNvbnRyYXN0DQoNCg0KYGBge3J9DQp0YWJsZXMgPC0gbGlzdCgpDQpmb3IgKGNvbnRyYXN0IGluIGNvbnRyYXN0cyl7DQpzaWdUYWJsZSA8LSByb3dEYXRhKHBlW1sicHRtUmVsIl1dKVtbY29udHJhc3RdXQ0KaWYobnJvdyhzaWdUYWJsZSAlPiUNCiAgbmEuZXhjbHVkZSAlPiUNCiAgZmlsdGVyKGFkalB2YWw8MC4wNSkpID4gMCl7DQpzaWdUYWJsZSA8LSBzaWdUYWJsZSAlPiUNCiAgbmEuZXhjbHVkZSAlPiUNCiAgZmlsdGVyKGFkalB2YWw8MC4wNSkgJT4lDQogIGFycmFuZ2UocHZhbCkgJT4lDQogIG11dGF0ZSgNCiAgICBzZSA9IGZvcm1hdChzZSwgZGlnaXRzID0gMiksIA0KICAgIGRmID0gZm9ybWF0KGRmLCBkaWdpdHMgPTIpLA0KICAgIHQgPSBmb3JtYXQodCwgZGlnaXRzID0gMiksDQogICAgYWRqUHZhbCA9IGZvcm1hdChhZGpQdmFsLCBkaWdpdHMgPSAzKSwNCiAgICByYW5rID0gMTpsZW5ndGgobG9nRkMpIA0KICApIA0Kc2lnVGFibGVfcHJpbnQgPC0gc2lnVGFibGUgJT4lIG11dGF0ZShwZXB0aWRvZm9ybSA9IHJvd25hbWVzKHNpZ1RhYmxlKSkgJT4lIGd0KCkgJT4lIHRhYl9oZWFkZXIodGl0bGUgPSBtZChjb250cmFzdCkpDQp0YWJsZXNbW2NvbnRyYXN0XV0gPC0gc2lnVGFibGUNCn0NCn0NCmtuaXRyOjprYWJsZSh0YWJsZXMpDQpgYGANCg0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KdGFibGVzX3RvQ3N2IDwtIGRvLmNhbGwoInJiaW5kIiwgdGFibGVzKQ0KdGFibGVzX3RvQ3N2IDwtIHRhYmxlc190b0NzdiAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb250cmFzdCIpDQp3cml0ZS5jc3YodGFibGVzX3RvQ3N2LCBmaWxlID0gIi9wdWJsaWMvY29tcG9taWNzL05pbmEvTXNxcm9iUFRNL3NpZ25pZmljYW5jZV90YWJsZXNfR1BfUFRNLmNzdiIpDQpgYGANCg0KDQoNCiMgUExPVFMNCg0KIyMjIExpbmVwbG90cw0KDQojIyMjIFBUTSAtIGxldmVsDQoNCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTB9DQpwdG1fbGlzdDEgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIGNvbnRyYXN0cyl7DQpzaWdUYWJsZSA8LSByb3dEYXRhKHBlW1sicHRtUmVsIl1dKVtbY29udHJhc3RdXSAlPiUgZmlsdGVyKGFkalB2YWw8MC4wNSkNCnB0bV9saXN0MSA8LSBjKHB0bV9saXN0MSwgcm93bmFtZXMoc2lnVGFibGUpKQ0KfQ0KcHRtX2xpc3QxIDwtIHVuaXF1ZShwdG1fbGlzdDEpDQoNCnBsb3RzIDwtIGxpc3QoKQ0KZm9yIChpIGluIHB0bV9saXN0MSl7DQpwcm90XyA9IHN0cl9zcGxpdChpLCAiICIpW1sxXV1bMV0NCnNpdGVfID0gc3RyX3NwbGl0X2ZpeGVkKGksICIgIiwgMilbLDJdDQoNCnBlcGZvcm1fZGYgPC0gbG9uZ0Zvcm1hdChwZVssLCJwZXBmb3JtUmVsIl0sIHJvd3ZhcnMgPSBjKCJMZWFkaW5nLnJhem9yLnByb3RlaW4iLCAicHRtIiksIGNvbHZhcnMgPSBjKCJjb25kaXRpb24iLCAic3Vic2V0IikpICU+JSBhcy5kYXRhLmZyYW1lKCkNCnBlcGZvcm1fZGYgPC0gcGVwZm9ybV9kZiAlPiUgZmlsdGVyKExlYWRpbmcucmF6b3IucHJvdGVpbiA9PSBwcm90XykNCnBlcGZvcm1fZGYgPC0gcGVwZm9ybV9kZiAlPiUgZmlsdGVyKGdyZXBsKHNpdGVfLCBwdG0sIGZpeGVkID0gVCkpDQpwZXBmb3JtX2RmJEZlYXR1cmVUeXBlIDwtICJQZXB0aWRlIg0KcGVwZm9ybV9kZiA8LSBwZXBmb3JtX2RmICU+JSBhcnJhbmdlKGNvbmRpdGlvbiwgc3Vic2V0LCByb3duYW1lKQ0KDQpwdG1fZGYgPC0gbG9uZ0Zvcm1hdChwZVssLCJwdG1SZWwiXSwgcm93dmFycyA9IGMoInByb3RlaW4iLCAicHRtIiksIGNvbHZhcnMgPSBjKCJjb25kaXRpb24iLCAic3Vic2V0IikpICU+JSBhcy5kYXRhLmZyYW1lKCkNCnB0bV9kZiA8LSBwdG1fZGYgJT4lIGZpbHRlcihwdG0gPT0gaSkNCnB0bV9kZiRGZWF0dXJlVHlwZSA8LSAiUFRNIg0KcHRtX2RmIDwtIHB0bV9kZiAlPiUgYXJyYW5nZShjb25kaXRpb24sIHN1YnNldCwgcm93bmFtZSkNCg0KcHRtX2VzdGltYXRlIDwtIHB0bV9kZiAlPiUgc2VsZWN0KGMoInByaW1hcnkiLCAiY29sbmFtZSIsICJjb25kaXRpb24iLCAic3Vic2V0IikpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKHJvd25hbWUgPSBwYXN0ZShwdG1fZGYkcm93bmFtZSwgImVzdGltYXRlIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHRtID0gcGFzdGUocHRtX2RmJHB0bSwgImVzdGltYXRlIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJvdGVpbiA9IHBhc3RlKHB0bV9kZiRQcm90ZWluLCAiZXN0aW1hdGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGZWF0dXJlVHlwZSA9ICJQVE1fZXN0aW1hdGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IE5BLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIm1vZGVsIikNCmZpeGVmZmVjdHMgPC0gcm93RGF0YShwZVtbInB0bVJlbCJdXSkkbXNxcm9iTW9kZWxzW1t1bmlxdWUocHRtX2RmJHB0bSldXSAlPiUgZ2V0Q29lZg0KcHRtX2VzdGltYXRlW3B0bV9lc3RpbWF0ZSRjb25kaXRpb249PSJCIiYocHRtX2VzdGltYXRlJHN1YnNldD09IngiKSxdJHZhbHVlIDwtIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpeGVmZmVjdHNbWyIoSW50ZXJjZXB0KSJdXQ0KcHRtX2VzdGltYXRlW3B0bV9lc3RpbWF0ZSRjb25kaXRpb249PSJCIiYocHRtX2VzdGltYXRlJHN1YnNldD09InkiKSxdJHZhbHVlIDwtIA0KICAgZml4ZWZmZWN0c1tbIihJbnRlcmNlcHQpIl1dICsgZml4ZWZmZWN0c1tbInN1YnNldHkiXV0NCnB0bV9lc3RpbWF0ZVtwdG1fZXN0aW1hdGUkY29uZGl0aW9uPT0iQSImKHB0bV9lc3RpbWF0ZSRzdWJzZXQ9PSJ4IiksXSR2YWx1ZSA8LSANCiAgIGZpeGVmZmVjdHNbWyIoSW50ZXJjZXB0KSJdXSArIGZpeGVmZmVjdHNbWyJjb25kaXRpb25BIl1dDQpwdG1fZXN0aW1hdGVbcHRtX2VzdGltYXRlJGNvbmRpdGlvbj09IkEiJihwdG1fZXN0aW1hdGUkc3Vic2V0PT0ieSIpLF0kdmFsdWUgPC0gDQogICBmaXhlZmZlY3RzW1siKEludGVyY2VwdCkiXV0gKyBmaXhlZmZlY3RzW1siY29uZGl0aW9uQSJdXSArIGZpeGVmZmVjdHNbWyJjb25kaXRpb25BOnN1YnNldHkiXV0gKyBmaXhlZmZlY3RzW1sic3Vic2V0eSJdXQ0KDQpwbG90X2RmIDwtIHJiaW5kbGlzdChsaXN0KHBlcGZvcm1fZGYsIHB0bV9kZiwgcHRtX2VzdGltYXRlKSwgZmlsbCA9IFRSVUUpDQpwbG90X3BvaW50cyA8LSByYmluZGxpc3QobGlzdChwZXBmb3JtX2RmLCBwdG1fZGYpLCBmaWxsID0gVFJVRSkNCg0KcGxvdF9kZiRwcmltYXJ5IDwtIGZvcmNhdHM6OmZjdF9pbm9yZGVyKHBsb3RfZGYkcHJpbWFyeSkNCg0KcGxvdHNbW2ldXSA8LSBwbG90X2RmICU+JSBnZ3Bsb3QoKSArDQogIGdlb21fbGluZShhZXMoeCA9IHByaW1hcnksIHkgPSB2YWx1ZSAsIGdyb3VwID0gcm93bmFtZSwgY29sb3IgPSBGZWF0dXJlVHlwZSksIHNpemUgPSAyKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IHBsb3RfcG9pbnRzLCBhZXMoeCA9IHByaW1hcnksIHkgPSB2YWx1ZSAsIGdyb3VwID0gcm93bmFtZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBGZWF0dXJlVHlwZSksIHNpemUgPSA1KSArDQogIGdlb21fdmxpbmUoZGF0YT1kYXRhLmZyYW1lKHggPSBjKDQzLjUsIDE3LjUsIDU2LjUpKSwNCiAgICAgICAgICAgICBhZXMoeGludGVyY2VwdD1hcy5udW1lcmljKHgpKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIlBlcHRpZGUiID0gIiNDM0MzQzMiLCAiUFRNIiA9ICJwYWxlZ3JlZW4zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQVE1fZXN0aW1hdGVkIiA9ICJzbGF0ZWJsdWUzIikpICsNCiAgbGFicyh0aXRsZSA9IGksIHggPSAiU2FtcGxlIiwgeSA9ICJJbnRlbnNpdHkiKSArDQogIHRoZW1lX2xpZ2h0KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDYwLCBoanVzdD0xLCBzaXplID0gMTApLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLA0KICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xOSksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwNCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE5KSwNCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLA0KICAgICAgICBsZWdlbmQudGl0bGUgPSAgZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNSwgMC4xKSkgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAyNCwgeSA9IDYuNSwgbGFiZWwgPSAiQiIsIHNpemUgPSA4KSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDcwLCB5ID0gNi41LCBsYWJlbCA9ICJBIiwgc2l6ZSA9IDgpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gOSwgeSA9IDYsIGxhYmVsID0gIngiLCBzaXplID0gNykgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAzMCwgeSA9IDYsIGxhYmVsID0gInkiLCBzaXplID0gNykgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSA0OS41LCB5ID0gNiwgbGFiZWwgPSAieCIsIHNpemUgPSA3KSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDczLCB5ID0gNiwgbGFiZWwgPSAieSIsIHNpemUgPSA3KSArDQogIHlsaW0oLTYsIDYuNSkNCg0KfQ0KcGxvdHMNCmBgYA0KDQojIyMjIFBlcHRpZG9mb3JtIGxldmVsDQoNCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTB9DQpwZXBmX2xpc3QxIDwtIGMoKQ0KZm9yIChjb250cmFzdCBpbiBjb250cmFzdHMpew0Kc2lnVGFibGUgPC0gcm93RGF0YShwZVtbInBlcGZvcm1SZWwiXV0pW1tjb250cmFzdF1dICU+JSBmaWx0ZXIoYWRqUHZhbDwwLjA1KQ0KcGVwZl9saXN0MSA8LSBjKHBlcGZfbGlzdDEsIHJvd25hbWVzKHNpZ1RhYmxlKSkNCn0NCnBlcGZfbGlzdDEgPC0gdW5pcXVlKHBlcGZfbGlzdDEpDQoNCnBsb3RzIDwtIGxpc3QoKQ0KZm9yIChpIGluIHBlcGZfbGlzdDEpew0KcHJvdF8gPSByb3dEYXRhKHBlW1sicGVwZm9ybVJlbCJdXSlbaSxdW1siTGVhZGluZy5yYXpvci5wcm90ZWluIl1dDQpzaXRlXyA9IHJvd0RhdGEocGVbWyJwZXBmb3JtUmVsIl1dKVtpLF1bWyJtb2QiXV0NCnNpdGVzXyA9IHN0cnNwbGl0KHNpdGVfLCAiLCAiKVtbMV1dDQpmb3IgKHNpdGVfIGluIHNpdGVzXyl7DQpwdG1fID0gcGFzdGUocHJvdF8sIHNpdGVfLCBjb2xsYXBzZSA9ICIgIikNCnByaW50KHB0bV8pDQoNCnBlcGZvcm1fZGYgPC0gbG9uZ0Zvcm1hdChwZVssLCJwZXBmb3JtUmVsIl0sIHJvd3ZhcnMgPSBjKCJMZWFkaW5nLnJhem9yLnByb3RlaW4iLCAicHRtIiksIGNvbHZhcnMgPSBjKCJjb25kaXRpb24iLCAic3Vic2V0IikpICU+JSBhcy5kYXRhLmZyYW1lKCkNCnBlcGZvcm1fZGYgPC0gcGVwZm9ybV9kZiAlPiUgZmlsdGVyKExlYWRpbmcucmF6b3IucHJvdGVpbiA9PSBwcm90XykNCnBlcGZvcm1fZGYgPC0gcGVwZm9ybV9kZiAlPiUgZmlsdGVyKGdyZXBsKHNpdGVfLCBwdG0sIGZpeGVkID0gVCkpDQpwZXBmb3JtX2RmJEZlYXR1cmVUeXBlIDwtICJQZXB0aWRlIg0KcGVwZm9ybV9kZltwZXBmb3JtX2RmJHJvd25hbWU9PWksXSRGZWF0dXJlVHlwZSA8LSAiU2lnbmlmaWNhbnRQZXB0aWRlIg0KcGVwZm9ybV9kZiA8LSBwZXBmb3JtX2RmICU+JSBhcnJhbmdlKGNvbmRpdGlvbiwgc3Vic2V0LCByb3duYW1lKQ0KDQpwdG1fZGYgPC0gbG9uZ0Zvcm1hdChwZVssLCJwdG1SZWwiXSwgcm93dmFycyA9IGMoInByb3RlaW4iLCAicHRtIiksIGNvbHZhcnMgPSBjKCJjb25kaXRpb24iLCAic3Vic2V0IikpICU+JSBhcy5kYXRhLmZyYW1lKCkNCnB0bV9kZiA8LSBwdG1fZGYgJT4lIGZpbHRlcihwdG0gPT0gcHRtXykNCnB0bV9kZiRGZWF0dXJlVHlwZSA8LSAiUFRNIg0KcHRtX2RmIDwtIHB0bV9kZiAlPiUgYXJyYW5nZShjb25kaXRpb24sIHN1YnNldCwgcm93bmFtZSkNCg0KcHRtX2VzdGltYXRlIDwtIHB0bV9kZiAlPiUgc2VsZWN0KGMoInByaW1hcnkiLCAiY29sbmFtZSIsICJjb25kaXRpb24iLCAic3Vic2V0IikpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKHJvd25hbWUgPSBwYXN0ZShwdG1fZGYkcm93bmFtZSwgImVzdGltYXRlIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHRtID0gcGFzdGUocHRtX2RmJHB0bSwgImVzdGltYXRlIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJvdGVpbiA9IHBhc3RlKHB0bV9kZiRQcm90ZWluLCAiZXN0aW1hdGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGZWF0dXJlVHlwZSA9ICJQVE1fZXN0aW1hdGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IE5BLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIm1vZGVsIikNCmZpeGVmZmVjdHMgPC0gcm93RGF0YShwZVtbInB0bVJlbCJdXSkkbXNxcm9iTW9kZWxzW1t1bmlxdWUocHRtX2RmJHB0bSldXSAlPiUgZ2V0Q29lZg0KcHRtX2VzdGltYXRlW3B0bV9lc3RpbWF0ZSRjb25kaXRpb249PSJCIiYocHRtX2VzdGltYXRlJHN1YnNldD09IngiKSxdJHZhbHVlIDwtIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpeGVmZmVjdHNbWyIoSW50ZXJjZXB0KSJdXQ0KcHRtX2VzdGltYXRlW3B0bV9lc3RpbWF0ZSRjb25kaXRpb249PSJCIiYocHRtX2VzdGltYXRlJHN1YnNldD09InkiKSxdJHZhbHVlIDwtIA0KICAgZml4ZWZmZWN0c1tbIihJbnRlcmNlcHQpIl1dICsgZml4ZWZmZWN0c1tbInN1YnNldHkiXV0NCnB0bV9lc3RpbWF0ZVtwdG1fZXN0aW1hdGUkY29uZGl0aW9uPT0iQSImKHB0bV9lc3RpbWF0ZSRzdWJzZXQ9PSJ4IiksXSR2YWx1ZSA8LSANCiAgIGZpeGVmZmVjdHNbWyIoSW50ZXJjZXB0KSJdXSArIGZpeGVmZmVjdHNbWyJjb25kaXRpb25BIl1dDQpwdG1fZXN0aW1hdGVbcHRtX2VzdGltYXRlJGNvbmRpdGlvbj09IkEiJihwdG1fZXN0aW1hdGUkc3Vic2V0PT0ieSIpLF0kdmFsdWUgPC0gDQogICBmaXhlZmZlY3RzW1siKEludGVyY2VwdCkiXV0gKyBmaXhlZmZlY3RzW1siY29uZGl0aW9uQSJdXSArIGZpeGVmZmVjdHNbWyJjb25kaXRpb25BOnN1YnNldHkiXV0gKyBmaXhlZmZlY3RzW1sic3Vic2V0eSJdXQ0KDQoNCnBsb3RfZGYgPC0gcmJpbmRsaXN0KGxpc3QocGVwZm9ybV9kZiwgcHRtX2RmLCBwdG1fZXN0aW1hdGUpLCBmaWxsID0gVFJVRSkNCnBsb3RfcG9pbnRzIDwtIHJiaW5kbGlzdChsaXN0KHBlcGZvcm1fZGYsIHB0bV9kZiksIGZpbGwgPSBUUlVFKQ0KDQoNCnBsb3RfZGYkcHJpbWFyeSA8LSBmb3JjYXRzOjpmY3RfaW5vcmRlcihwbG90X2RmJHByaW1hcnkpDQoNCnBsb3RzW1twYXN0ZShpLCBzaXRlXyldXSA8LSBwbG90X2RmICU+JSBnZ3Bsb3QoKSArDQogIGdlb21fbGluZShhZXMoeCA9IHByaW1hcnksIHkgPSB2YWx1ZSAsIGdyb3VwID0gcm93bmFtZSwgY29sb3IgPSBGZWF0dXJlVHlwZSksIHNpemUgPTIpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gcGxvdF9wb2ludHMsIGFlcyh4ID0gcHJpbWFyeSwgeSA9IHZhbHVlICwgZ3JvdXAgPSByb3duYW1lLCBjb2xvciA9IEZlYXR1cmVUeXBlKSwgc2l6ZSA9IDUpICsNCiAgZ2VvbV92bGluZShkYXRhPWRhdGEuZnJhbWUoeCA9IGMoNDMuNSwgMTcuNSwgNTYuNSkpLA0KICAgICAgICAgICAgIGFlcyh4aW50ZXJjZXB0PWFzLm51bWVyaWMoeCkpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiUGVwdGlkZSIgPSAiI0MzQzNDMyIsICJQVE0iID0gInBhbGVncmVlbjMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTaWduaWZpY2FudFBlcHRpZGUiID0gInZpb2xldHJlZDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBUTV9lc3RpbWF0ZWQiID0gImxpZ2h0Z29sZGVucm9kIikpICsNCiAgbGFicyh0aXRsZSA9IHBhc3RlKGksIHB0bV8pLCB4ID0gIlNhbXBsZSIsIHkgPSAiSW50ZW5zaXR5IikgKw0KICB0aGVtZV9saWdodCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgaGp1c3Q9MSwgc2l6ZSA9IDEwKSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTkpLA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksDQogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOSksDQogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwNCiAgICAgICAgbGVnZW5kLnRpdGxlID0gIGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjUsIDAuMSkpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMjQsIHkgPSA2LjUsIGxhYmVsID0gIkIiLCBzaXplID0gOCkgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSA3MCwgeSA9IDYuNSwgbGFiZWwgPSAiQSIsIHNpemUgPSA4KSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDksIHkgPSA2LCBsYWJlbCA9ICJ4Iiwgc2l6ZSA9IDcpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMzAsIHkgPSA2LCBsYWJlbCA9ICJ5Iiwgc2l6ZSA9IDcpICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gNDkuNSwgeSA9IDYsIGxhYmVsID0gIngiLCBzaXplID0gNykgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSA3MywgeSA9IDYsIGxhYmVsID0gInkiLCBzaXplID0gNykgKw0KICB5bGltKC02LCA2LjUpDQp9DQp9DQpwbG90cw0KYGBgDQoNCg==